import { parseLocale, type EmailLocale } from '@/lib/email/locale';
import { parseStrictEmail } from '@/lib/validation/email';
import {
  isRecord,
  isBotSubmission,
  unknownFieldErrors,
  optionalInt,
  optionalString,
  requiredInt,
  requiredPositiveNumber,
  requiredString,
} from '@/lib/validation/fields';
import {
  CATERING_PACKAGES,
  CONTACT_SUBJECTS,
  FIELD_LIMITS,
  type CateringPackageValue,
  type ContactSubjectValue,
} from '@/lib/validation/limits';
import { parseValidPhone } from '@/lib/validation/phone';

export { isBotSubmission };

export interface ParsedContactInput {
  name: string;
  email: string;
  phone?: string;
  subject: ContactSubjectValue;
  message: string;
}

export interface ParsedReservationInput {
  customer_name: string;
  customer_email?: string;
  customer_phone: string;
  reservation_date: string;
  reservation_time: string;
  guest_count: number;
  special_requests?: string;
  preferred_language: EmailLocale;
}

export interface ParsedCateringInput {
  contact_name: string;
  contact_email: string;
  contact_phone?: string;
  event_type?: string;
  event_date?: string;
  guest_count?: number;
  package_type?: CateringPackageValue;
  budget_per_person?: number;
  message?: string;
  preferred_language: EmailLocale;
  selected_items?: { item_id: number; people?: number; quantity?: number }[];
}

export interface ParsedOrderItemInput {
  menu_item_id?: number;
  item_name: string;
  item_price: number;
  quantity: number;
  people?: number;
}

export interface ParsedOrderInput {
  customer_name: string;
  customer_phone: string;
  pickup_time: string;
  notes?: string;
  total_amount: number;
  preferred_language: EmailLocale;
  payment_method: 'pay_in_store' | 'stripe';
  items: ParsedOrderItemInput[];
}

export interface ParsedReviewInput {
  author_name: string;
  quote: string;
  rating: number;
  event_label?: string;
}

const CONTACT_KEYS = ['name', 'email', 'phone', 'subject', 'message'] as const;
const RESERVATION_KEYS = [
  'customer_name',
  'customer_email',
  'customer_phone',
  'reservation_date',
  'reservation_time',
  'guest_count',
  'special_requests',
  'language',
] as const;
const CATERING_KEYS = [
  'contact_name',
  'contact_email',
  'contact_phone',
  'event_type',
  'event_date',
  'guest_count',
  'package_type',
  'budget_per_person',
  'message',
  'language',
  'selected_items',
] as const;
const ORDER_KEYS = [
  'customer_name',
  'customer_phone',
  'pickup_time',
  'notes',
  'total_amount',
  'items',
  'language',
  'payment_method',
] as const;
const REVIEW_KEYS = ['author_name', 'quote', 'rating', 'event_label'] as const;

function asBody(raw: unknown): { body: Record<string, unknown>; errors: string[] } {
  if (!isRecord(raw)) {
    return { body: {}, errors: ['Ungültige Anfrage.'] };
  }
  return { body: raw, errors: [] };
}

export function parseContactBody(raw: unknown): {
  data: ParsedContactInput | null;
  errors: string[];
} {
  const { body, errors: base } = asBody(raw);
  if (base.length) return { data: null, errors: base };

  const errors = [...unknownFieldErrors(body, CONTACT_KEYS)];
  const name = requiredString(body.name, FIELD_LIMITS.name);
  if (!name.ok) errors.push('Name ist erforderlich.');

  const email = parseStrictEmail(body.email);
  if (!email) errors.push('Ungültige E-Mail-Adresse.');

  const phone = optionalString(body.phone, FIELD_LIMITS.phone);
  if (!phone.ok) errors.push('Ungültige Telefonnummer.');
  const e164 = phone.ok && phone.value ? parseValidPhone(phone.value) : null;
  if (phone.ok && phone.value && !e164) errors.push('Ungültige Telefonnummer.');

  const subject =
    typeof body.subject === 'string' &&
    (CONTACT_SUBJECTS as readonly string[]).includes(body.subject)
      ? (body.subject as ContactSubjectValue)
      : null;
  if (!subject) errors.push('Betreff ist erforderlich.');

  const message = requiredString(body.message, FIELD_LIMITS.message);
  if (!message.ok) errors.push('Nachricht ist erforderlich.');

  if (errors.length || !name.ok || !email || !subject || !message.ok) {
    return { data: null, errors };
  }

  return {
    data: {
      name: name.value,
      email,
      phone: e164 ?? undefined,
      subject,
      message: message.value,
    },
    errors: [],
  };
}

export function parseReservationBody(raw: unknown): {
  data: ParsedReservationInput | null;
  errors: string[];
} {
  const { body, errors: base } = asBody(raw);
  if (base.length) return { data: null, errors: base };

  const errors = [...unknownFieldErrors(body, RESERVATION_KEYS)];
  const name = requiredString(body.customer_name, FIELD_LIMITS.name);
  if (!name.ok) errors.push('Name ist erforderlich.');

  const phone = requiredString(body.customer_phone, FIELD_LIMITS.phone);
  const e164 = phone.ok ? parseValidPhone(phone.value) : null;
  if (!phone.ok) errors.push('Telefonnummer ist erforderlich.');
  else if (!e164) errors.push('Ungültige Telefonnummer.');

  let customer_email: string | undefined;
  if (body.customer_email) {
    const parsed = parseStrictEmail(body.customer_email);
    if (!parsed) errors.push('Ungültige E-Mail-Adresse.');
    else customer_email = parsed;
  }

  const date = requiredString(body.reservation_date, 10);
  if (!date.ok || !/^\d{4}-\d{2}-\d{2}$/.test(date.value)) {
    errors.push('Datum ist erforderlich.');
  }

  const time = requiredString(body.reservation_time, 8);
  if (!time.ok) errors.push('Uhrzeit ist erforderlich.');

  const guests = requiredInt(
    body.guest_count,
    FIELD_LIMITS.guestCountMin,
    FIELD_LIMITS.guestCountMax
  );
  if (!guests.ok) errors.push('Personenzahl muss zwischen 1 und 50 liegen.');

  const notes = optionalString(body.special_requests, FIELD_LIMITS.message);
  if (!notes.ok) errors.push('Anfrage ist zu lang.');

  if (date.ok) {
    const parsedDate = new Date(date.value);
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    if (Number.isNaN(parsedDate.getTime()) || parsedDate < today) {
      errors.push('Datum darf nicht in der Vergangenheit liegen.');
    }
  }

  if (
    errors.length ||
    !name.ok ||
    !phone.ok ||
    !e164 ||
    !date.ok ||
    !time.ok ||
    !guests.ok ||
    !notes.ok
  ) {
    return { data: null, errors };
  }

  return {
    data: {
      customer_name: name.value,
      customer_email,
      customer_phone: e164,
      reservation_date: date.value,
      reservation_time: time.value,
      guest_count: guests.value,
      special_requests: notes.value,
      preferred_language: parseLocale(body.language),
    },
    errors: [],
  };
}

export function parseCateringBody(raw: unknown): {
  data: ParsedCateringInput | null;
  errors: string[];
} {
  const { body, errors: base } = asBody(raw);
  if (base.length) return { data: null, errors: base };

  const errors = [...unknownFieldErrors(body, CATERING_KEYS)];
  const name = requiredString(body.contact_name, FIELD_LIMITS.name);
  if (!name.ok) errors.push('Name ist erforderlich.');

  const email = parseStrictEmail(body.contact_email);
  if (!email) errors.push('Ungültige E-Mail-Adresse.');

  const phone = optionalString(body.contact_phone, FIELD_LIMITS.phone);
  if (!phone.ok) errors.push('Ungültige Telefonnummer.');
  const e164 = phone.ok && phone.value ? parseValidPhone(phone.value) : null;
  if (phone.ok && phone.value && !e164) errors.push('Ungültige Telefonnummer.');

  const eventType = optionalString(body.event_type, FIELD_LIMITS.eventType);
  if (!eventType.ok) errors.push('Ereignistyp ist ungültig.');

  const eventDate = optionalString(body.event_date, 10);
  if (!eventDate.ok) errors.push('Ungültiges Datum.');
  if (eventDate.ok && eventDate.value && !/^\d{4}-\d{2}-\d{2}$/.test(eventDate.value)) {
    errors.push('Ungültiges Datum.');
  }

  const guests = optionalInt(
    body.guest_count,
    FIELD_LIMITS.cateringGuestMin,
    FIELD_LIMITS.cateringGuestMax
  );
  if (!guests.ok) {
    errors.push(
      `Personenzahl muss zwischen ${FIELD_LIMITS.cateringGuestMin} und ${FIELD_LIMITS.cateringGuestMax} liegen.`
    );
  }

  let package_type: CateringPackageValue | undefined;
  if (body.package_type) {
    if (
      typeof body.package_type === 'string' &&
      (CATERING_PACKAGES as readonly string[]).includes(body.package_type)
    ) {
      package_type = body.package_type as CateringPackageValue;
    } else {
      errors.push('Ungültiges Paket.');
    }
  }

  const budget = optionalInt(body.budget_per_person, 1, 10_000);
  if (!budget.ok) errors.push('Ungültiges Budget.');

  const message = optionalString(body.message, FIELD_LIMITS.message);
  if (!message.ok) errors.push('Nachricht ist zu lang.');

  let selected_items: { item_id: number; people?: number; quantity?: number }[] | undefined;
  if (body.selected_items !== undefined) {
    if (!Array.isArray(body.selected_items)) {
      errors.push('Ungültige Auswahl.');
    } else if (body.selected_items.length > FIELD_LIMITS.cateringSelectionMax) {
      errors.push('Zu viele Artikel ausgewählt.');
    } else {
      selected_items = [];
      for (const row of body.selected_items) {
        if (!isRecord(row)) {
          errors.push('Ungültige Auswahl.');
          break;
        }
        const itemId = Number(row.item_id);
        if (!Number.isInteger(itemId) || itemId <= 0) {
          errors.push('Ungültige Artikel-ID.');
          break;
        }
        const qty = row.quantity === undefined ? 1 : Number(row.quantity);
        if (!Number.isInteger(qty) || qty < 1 || qty > 500) {
          errors.push('Ungültige Menge.');
          break;
        }
        let people: number | undefined;
        if (row.people !== undefined) {
          const p = Number(row.people);
          if (
            !Number.isInteger(p) ||
            p < FIELD_LIMITS.cateringGuestMin ||
            p > FIELD_LIMITS.cateringGuestMax
          ) {
            errors.push(
              `Personenzahl muss zwischen ${FIELD_LIMITS.cateringGuestMin} und ${FIELD_LIMITS.cateringGuestMax} liegen.`
            );
            break;
          }
          people = p;
        }
        selected_items.push({ item_id: itemId, people, quantity: qty });
      }
    }
  }

  // Derive guest_count from max line people when omitted but selection has people
  let derivedGuestCount = guests.ok ? guests.value : undefined;
  if (
    (derivedGuestCount == null || !Number.isFinite(derivedGuestCount)) &&
    selected_items?.length
  ) {
    const peopleValues = selected_items
      .map((s) => s.people)
      .filter((p): p is number => typeof p === 'number');
    if (peopleValues.length) {
      derivedGuestCount = Math.max(...peopleValues);
    }
  }

  if (
    selected_items &&
    selected_items.length > 0 &&
    (derivedGuestCount == null || !Number.isFinite(derivedGuestCount))
  ) {
    errors.push(
      `Personenzahl muss zwischen ${FIELD_LIMITS.cateringGuestMin} und ${FIELD_LIMITS.cateringGuestMax} liegen.`
    );
  }

  if (
    errors.length ||
    !name.ok ||
    !email ||
    !phone.ok ||
    !eventType.ok ||
    !eventDate.ok ||
    !guests.ok ||
    !message.ok
  ) {
    return { data: null, errors };
  }

  return {
    data: {
      contact_name: name.value,
      contact_email: email,
      contact_phone: e164 ?? undefined,
      event_type: eventType.value,
      event_date: eventDate.value,
      guest_count: derivedGuestCount ?? guests.value,
      package_type,
      budget_per_person: budget.ok ? budget.value : undefined,
      message: message.value,
      preferred_language: parseLocale(body.language),
      selected_items,
    },
    errors: [],
  };
}

export function parseOrderBody(raw: unknown): { data: ParsedOrderInput | null; errors: string[] } {
  const { body, errors: base } = asBody(raw);
  if (base.length) return { data: null, errors: base };

  const errors = [...unknownFieldErrors(body, ORDER_KEYS)];
  const name = requiredString(body.customer_name, FIELD_LIMITS.name);
  if (!name.ok) errors.push('Name ist erforderlich.');

  const phone = requiredString(body.customer_phone, FIELD_LIMITS.phone);
  const e164 = phone.ok ? parseValidPhone(phone.value) : null;
  if (!phone.ok) errors.push('Telefonnummer ist erforderlich.');
  else if (!e164) errors.push('Ungültige Telefonnummer.');

  const pickup = requiredString(body.pickup_time, FIELD_LIMITS.pickupTime);
  if (!pickup.ok) errors.push('Abholzeit ist erforderlich.');

  const notes = optionalString(body.notes, FIELD_LIMITS.message);
  if (!notes.ok) errors.push('Notiz ist zu lang.');

  const total = requiredPositiveNumber(body.total_amount);
  if (!total.ok) errors.push('Gesamtbetrag muss größer als 0 sein.');

  if (!Array.isArray(body.items) || body.items.length === 0) {
    errors.push('Warenkorb ist leer.');
  } else if (body.items.length > FIELD_LIMITS.cartItemsMax) {
    errors.push('Zu viele Artikel im Warenkorb.');
  }

  const items: ParsedOrderItemInput[] = [];
  if (Array.isArray(body.items)) {
    for (const item of body.items.slice(0, FIELD_LIMITS.cartItemsMax + 1)) {
      if (!isRecord(item)) {
        errors.push('Ungültiger Artikel.');
        continue;
      }
      const itemName = requiredString(item.item_name, FIELD_LIMITS.name);
      if (!itemName.ok) errors.push('Artikelname fehlt.');
      const price = requiredPositiveNumber(item.item_price);
      if (!price.ok)
        errors.push(`Ungültiger Preis für "${itemName.ok ? itemName.value : 'Artikel'}".`);
      const qty = requiredInt(item.quantity, FIELD_LIMITS.quantityMin, FIELD_LIMITS.quantityMax);
      if (!qty.ok) {
        errors.push(`Ungültige Menge für "${itemName.ok ? itemName.value : 'Artikel'}".`);
      }
      const menuId =
        item.menu_item_id === undefined || item.menu_item_id === null
          ? undefined
          : Number(item.menu_item_id);
      if (item.menu_item_id != null && !Number.isInteger(menuId)) {
        errors.push('Artikel-ID fehlt.');
      }
      if (itemName.ok && price.ok && qty.ok) {
        let people: number | undefined;
        if (item.people !== undefined && item.people !== null && item.people !== '') {
          const peopleParsed = requiredInt(item.people, 1, 30);
          if (!peopleParsed.ok) {
            errors.push(
              `Ungültige Personenanzahl für "${itemName.ok ? itemName.value : 'Artikel'}".`
            );
          } else {
            people = peopleParsed.value;
          }
        }
        items.push({
          menu_item_id: Number.isInteger(menuId) ? menuId : undefined,
          item_name: itemName.value,
          item_price: price.value,
          quantity: qty.value,
          ...(people !== undefined ? { people } : {}),
        });
      }
    }
  }

  if (errors.length || !name.ok || !phone.ok || !e164 || !pickup.ok || !notes.ok || !total.ok) {
    return { data: null, errors };
  }

  const paymentMethodRaw =
    body.payment_method === undefined || body.payment_method === null
      ? 'pay_in_store'
      : body.payment_method;
  if (paymentMethodRaw !== 'pay_in_store' && paymentMethodRaw !== 'stripe') {
    errors.push('Ungültige Zahlungsmethode.');
    return { data: null, errors };
  }

  return {
    data: {
      customer_name: name.value,
      customer_phone: e164,
      pickup_time: pickup.value,
      notes: notes.value,
      total_amount: total.value,
      preferred_language: parseLocale(body.language),
      payment_method: paymentMethodRaw,
      items,
    },
    errors: [],
  };
}

export function parseReviewBody(raw: unknown): {
  data: ParsedReviewInput | null;
  errors: string[];
} {
  const { body, errors: base } = asBody(raw);
  if (base.length) return { data: null, errors: base };

  const errors = [...unknownFieldErrors(body, REVIEW_KEYS)];
  const name = requiredString(body.author_name, FIELD_LIMITS.name);
  if (!name.ok) errors.push('Name ist erforderlich.');

  const quote = requiredString(body.quote, FIELD_LIMITS.reviewQuote);
  if (!quote.ok) errors.push('Bewertungstext ist erforderlich.');

  const rating = requiredInt(body.rating, 1, 5);
  if (!rating.ok) errors.push('Sternebewertung (1–5) ist erforderlich.');

  const event = optionalString(body.event_label, FIELD_LIMITS.reviewEvent);
  if (!event.ok) errors.push('Anlass ist ungültig.');

  if (errors.length || !name.ok || !quote.ok || !rating.ok || !event.ok) {
    return { data: null, errors };
  }

  return {
    data: {
      author_name: name.value,
      quote: quote.value,
      rating: rating.value,
      event_label: event.value,
    },
    errors: [],
  };
}
