// ─── RSVP Form Config: shared types + validator ─────────────────────────────
//
// Stored on Event.rsvpFormConfig as JSON. When null/absent, the public RSVP
// page falls back to the default static form (name, email, mobile, guests,
// optional category).
//
// In custom mode the organizer designs additional questions. Name + Mobile are
// always implicitly required (handled in submitRsvp) and the category dropdown
// is auto-rendered when the event has ticket categories.

export type FieldType =
  | 'text'        // short single-line text
  | 'textarea'    // long multi-line text
  | 'email'
  | 'tel'
  | 'number'
  | 'select'      // single-value dropdown
  | 'multiselect' // checkbox group, value = string[]
  | 'radio'       // single value chosen from list
  | 'date'
  | 'time';

export interface FieldValidation {
  minLength?: number;
  maxLength?: number;
  min?: number;
  max?: number;
}

export interface FieldDef {
  id: string;                  // unique within the form (React key)
  key: string;                 // storage key. If in BUILTIN_KEYS, maps to Attendee column; otherwise → additionalInfo[key]
  type: FieldType;
  label: string;
  placeholder?: string;
  helpText?: string;
  required?: boolean;
  options?: string[];          // for select / multiselect / radio
  validation?: FieldValidation;
}

export interface RsvpFormConfig {
  mode: 'default' | 'custom';
  fields: FieldDef[];          // in display order; only used when mode === 'custom'
  submitButtonLabel?: string;
  successMessage?: string;
}

export const BUILTIN_KEYS = ['email', 'companyName', 'jobTitle', 'numberOfGuests'] as const;
export type BuiltinKey = typeof BUILTIN_KEYS[number];

export function isBuiltinKey(key: string): key is BuiltinKey {
  return (BUILTIN_KEYS as readonly string[]).includes(key);
}

// ─── Server-side validation ──────────────────────────────────────────────────
//
// Returns { ok, errors } where errors is map of fieldId → error message.

export interface ValidationResult {
  ok: boolean;
  errors: Record<string, string>;
}

function isEmptyValue(v: unknown): boolean {
  if (v === undefined || v === null) return true;
  if (typeof v === 'string') return v.trim().length === 0;
  if (Array.isArray(v)) return v.length === 0;
  return false;
}

function validateField(field: FieldDef, raw: unknown): string | null {
  // Required check
  if (field.required && isEmptyValue(raw)) {
    return `${field.label} is required`;
  }
  if (isEmptyValue(raw)) return null; // skip further checks on empty optional values

  const validation = field.validation || {};

  switch (field.type) {
    case 'text':
    case 'textarea': {
      if (typeof raw !== 'string') return `${field.label} must be text`;
      const s = raw.trim();
      if (validation.minLength != null && s.length < validation.minLength) return `${field.label} must be at least ${validation.minLength} characters`;
      if (validation.maxLength != null && s.length > validation.maxLength) return `${field.label} must be at most ${validation.maxLength} characters`;
      return null;
    }
    case 'email': {
      if (typeof raw !== 'string') return `${field.label} must be an email`;
      const s = raw.trim();
      const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!re.test(s)) return `${field.label} is not a valid email`;
      return null;
    }
    case 'tel': {
      if (typeof raw !== 'string') return `${field.label} must be a phone number`;
      const digits = raw.replace(/\D/g, '');
      if (digits.length < 7) return `${field.label} is not a valid phone number`;
      return null;
    }
    case 'number': {
      const n = typeof raw === 'number' ? raw : Number(raw);
      if (!Number.isFinite(n)) return `${field.label} must be a number`;
      if (validation.min != null && n < validation.min) return `${field.label} must be at least ${validation.min}`;
      if (validation.max != null && n > validation.max) return `${field.label} must be at most ${validation.max}`;
      return null;
    }
    case 'select':
    case 'radio': {
      if (typeof raw !== 'string') return `${field.label} is not a valid choice`;
      if (field.options && !field.options.includes(raw)) return `${field.label} is not a valid choice`;
      return null;
    }
    case 'multiselect': {
      if (!Array.isArray(raw)) return `${field.label} is not a valid selection`;
      const opts = field.options || [];
      for (const item of raw) {
        if (typeof item !== 'string' || !opts.includes(item)) return `${field.label} contains an invalid choice`;
      }
      return null;
    }
    case 'date':
    case 'time': {
      if (typeof raw !== 'string') return `${field.label} is invalid`;
      return null;
    }
    default:
      return null;
  }
}

export function validateRsvpSubmission(
  config: RsvpFormConfig,
  data: Record<string, unknown>
): ValidationResult {
  if (config.mode !== 'custom' || !Array.isArray(config.fields)) {
    return { ok: true, errors: {} };
  }
  const errors: Record<string, string> = {};
  for (const field of config.fields) {
    const value = data[field.key];
    const err = validateField(field, value);
    if (err) errors[field.id] = err;
  }
  return { ok: Object.keys(errors).length === 0, errors };
}

// ─── Helper: split submitted values into Attendee columns vs additionalInfo ──
//
// Given a custom-form submission, distributes values:
//  - Built-in keys (email, companyName, jobTitle, numberOfGuests) → core
//  - Custom keys                                                  → additionalInfo
//
// Returns { core, additionalInfo } where `core` is partial Attendee data.

export interface SplitSubmission {
  core: {
    email?: string | null;
    companyName?: string | null;
    jobTitle?: string | null;
    numberOfGuests?: number;
  };
  additionalInfo: Record<string, unknown>;
}

export function splitSubmission(
  config: RsvpFormConfig,
  data: Record<string, unknown>
): SplitSubmission {
  const core: SplitSubmission['core'] = {};
  const additionalInfo: Record<string, unknown> = {};

  if (config.mode !== 'custom' || !Array.isArray(config.fields)) {
    return { core, additionalInfo };
  }

  for (const field of config.fields) {
    const value = data[field.key];
    if (value === undefined || value === null) continue;
    if (isBuiltinKey(field.key)) {
      switch (field.key) {
        case 'email':           core.email = typeof value === 'string' ? value.trim() || null : null; break;
        case 'companyName':     core.companyName = typeof value === 'string' ? value.trim() || null : null; break;
        case 'jobTitle':        core.jobTitle = typeof value === 'string' ? value.trim() || null : null; break;
        case 'numberOfGuests':  core.numberOfGuests = Number(value) || 1; break;
      }
    } else {
      additionalInfo[field.key] = value;
    }
  }

  return { core, additionalInfo };
}
