'use client';

import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Input, InputBase, InputGroup, NativeSelect, Textarea, PrivacyCheckbox } from '../elements';
import type { FormDefinition, FormFieldDefinition } from '../../types/api/form';
import countries from '../../utils/countries';

export interface DynamicFormFieldsProps {
  /** Form definition from API (fields array + optional settings). */
  form: FormDefinition;
  /** For job_application forms: add hidden jobSlug input. */
  jobSlug?: string;
  /** Optional URLs for ToS/Privacy links in consent checkbox label. */
  privacyPolicyUrl?: string;
  termsOfServiceUrl?: string;
}

const INPUT_TYPES = ['text', 'email', 'tel'] as const;

/** Get national-format mask from country (e.g. "(###) ###-####") by stripping country code from phoneMask. */
function getNationalMask(country: (typeof countries)[0] | undefined): string {
  if (!country?.phoneMask) return '';
  const code = country.phoneCode.startsWith('+') ? country.phoneCode : `+${country.phoneCode}`;
  const escaped = code.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  return country.phoneMask.replace(new RegExp(`^\\s*${escaped}[\\s-]*`), '').trim();
}

/** Format digit string into mask; # = one digit. No trailing literals so backspace works. */
function formatDigitsToMask(digits: string, mask: string): string {
  if (digits.length === 0) return '';
  let i = 0;
  let out = '';
  for (const c of mask) {
    if (c === '#') {
      if (i < digits.length) out += digits[i++];
      else break;
    } else if (i < digits.length) {
      out += c;
    }
  }
  return out;
}

function allFieldsFlat(fields: FormDefinition['fields']): FormFieldDefinition[] {
  const out: FormFieldDefinition[] = [];
  for (const item of fields) {
    if (Array.isArray(item)) out.push(...item);
    else if (item && typeof item === 'object' && 'name' in item) out.push(item);
  }
  return out;
}

function renderField(
  field: FormFieldDefinition,
  index: number,
  showCountryCode: boolean,
  selectedCountryPhone: string,
  onCountryChange: (code: string) => void,
  phoneValues: Record<string, string>,
  setPhoneValues: React.Dispatch<React.SetStateAction<Record<string, string>>>,
  companyName: string,
  privacyPolicyUrl: string | undefined,
  termsOfServiceUrl: string | undefined
): React.ReactNode {
  const name = field.name ?? `field-${index}`;
  const type = (field.type ?? 'text').toString().toLowerCase();

  if (field.type === 'hidden') {
    const val = field.value ?? '';
    return <input key={name} type="hidden" name={name} value={val} />;
  }

  if (type === 'checkbox') {
    const labelRaw = field.label ?? '';
    const companyNameClean = companyName.replace(/\*\*/g, '').trim();
    let labelWithCompany = companyNameClean
      ? labelRaw.replace(/\{\{company_name\}\}/gi, companyNameClean)
      : labelRaw;
    // Inject ToS/Privacy links as markdown so they render as links (then whole label is rendered as markdown)
    if (name === 'tos_privacy_consent' && privacyPolicyUrl && termsOfServiceUrl) {
      labelWithCompany = labelWithCompany
        .replace(/\*\*Terms of Service\*\*/gi, `**[Terms of Service](${termsOfServiceUrl})**`)
        .replace(/\*\*Privacy Policy\*\*/gi, `**[Privacy Policy](${privacyPolicyUrl})**`);
    }
    const id = `checkbox-${name}-${index}`;
    return (
      <div key={name} className="flex items-start gap-3">
        <input
          type="checkbox"
          id={id}
          name={name}
          value="on"
          required={Boolean(field.required)}
          aria-describedby={id ? `${id}-desc` : undefined}
          className="mt-1 h-4 w-4 shrink-0 rounded border-secondary focus:ring-focus-ring"
        />
        <label
          id={id ? `${id}-desc` : undefined}
          htmlFor={id}
          className="font-body text-sm text-tertiary [&_a]:underline [&_a]:outline-focus-ring [&_strong]:font-semibold"
        >
          <ReactMarkdown
            remarkPlugins={[remarkGfm]}
            components={{
              p: ({ children }) => <span>{children}</span>,
              strong: (props) => <strong className="font-semibold" {...props} />,
              a: (props) => <a {...props} className="underline outline-focus-ring focus-visible:outline-2 focus-visible:outline-offset-2" />,
            }}
          >
            {labelWithCompany}
          </ReactMarkdown>
        </label>
      </div>
    );
  }

  if (field.type === 'tel' && showCountryCode) {
    const countryOptions = countries.map((c) => ({
      label: c.phoneCode.startsWith('+') ? c.phoneCode : `+${c.phoneCode}`,
      value: c.code,
    }));
    const country = countries.find((c) => c.code === selectedCountryPhone);
    const nationalMask = getNationalMask(country);
    const value = phoneValues[name] ?? '';
    const placeholder = nationalMask ? nationalMask.replace(/#/g, '0') : (field.placeholder ?? '');

    const handlePhoneChange = (valueOrEvent: unknown) => {
      // Some input implementations call onChange with an event, others with a string value.
      // Normalize defensively to avoid runtime errors.
      const raw =
        typeof valueOrEvent === 'string'
          ? valueOrEvent
          : (valueOrEvent &&
              typeof valueOrEvent === 'object' &&
              'target' in valueOrEvent &&
              (valueOrEvent as { target?: { value?: unknown } }).target &&
              typeof (valueOrEvent as { target: { value?: unknown } }).target.value === 'string'
              ? (valueOrEvent as { target: { value: string } }).target.value
              : '');

      const digits = raw.replace(/\D/g, '');
      const formatted = nationalMask ? formatDigitsToMask(digits, nationalMask) : digits;
      setPhoneValues((prev) => ({ ...prev, [name]: formatted }));
    };

    return (
      <InputGroup
        key={name}
        label={field.label}
        isRequired={Boolean(field.required)}
        size="md"
        leadingAddon={
          <NativeSelect
            aria-label="Country code"
            value={selectedCountryPhone}
            onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
              onCountryChange(e.currentTarget.value)
            }
            options={countryOptions}
          />
        }
      >
        <InputBase
          type="tel"
          name={name}
          value={value}
          onChange={handlePhoneChange}
          placeholder={placeholder}
          size="md"
        />
      </InputGroup>
    );
  }

  if (field.type === 'textarea') {
    return (
      <Textarea
        key={name}
        name={name}
        label={field.label}
        placeholder={field.placeholder}
        rows={6}
        isRequired={Boolean(field.required)}
      />
    );
  }

  const inputType = INPUT_TYPES.includes(field.type as (typeof INPUT_TYPES)[number])
    ? (field.type as 'text' | 'email' | 'tel')
    : 'text';
  return (
    <Input
      key={name}
      isRequired={Boolean(field.required)}
      size="md"
      name={name}
      label={field.label}
      type={inputType}
      placeholder={field.placeholder}
    />
  );
}

export function DynamicFormFields({ form, jobSlug, privacyPolicyUrl, termsOfServiceUrl }: DynamicFormFieldsProps) {
  const [selectedCountryPhone, setSelectedCountryPhone] = useState('US');
  const [phoneValues, setPhoneValues] = useState<Record<string, string>>({});
  const { settings } = form;
  const fields: FormDefinition['fields'] = Array.isArray(form.fields) ? form.fields : [];
  const companyName = form.company_name ?? '';

  const handleCountryChange = (newCode: string) => {
    setSelectedCountryPhone(newCode);
    const country = countries.find((c) => c.code === newCode);
    const nationalMask = getNationalMask(country);
    if (!nationalMask) return;
    setPhoneValues((prev) => {
      const next = { ...prev };
      let changed = false;
      for (const name of Object.keys(next)) {
        const digits = next[name].replace(/\D/g, '');
        const formatted = formatDigitsToMask(digits, nationalMask);
        if (formatted !== next[name]) {
          next[name] = formatted;
          changed = true;
        }
      }
      return changed ? next : prev;
    });
  };

  /** Show country code selector for tel fields; default true (US +1) unless backend sets to false. */
  const showCountryCode = settings?.show_country_code !== false;
  const flat = allFieldsFlat(fields);
  const hasPhoneField = flat.some((f) => f.type === 'tel');
  const hasCheckboxFields = flat.some((f) => (f.type ?? '').toString().toLowerCase() === 'checkbox');
  const showPrivacyCheckbox =
    settings?.show_privacy_checkbox !== false && hasPhoneField && !hasCheckboxFields;

  const renderFieldWithProps = (item: FormFieldDefinition, i: number) =>
    renderField(
      item,
      i,
      showCountryCode,
      selectedCountryPhone,
      handleCountryChange,
      phoneValues,
      setPhoneValues,
      companyName,
      privacyPolicyUrl,
      termsOfServiceUrl
    );

  return (
    <div className="flex flex-col gap-6">
      {fields.map((item, index) => {
        if (Array.isArray(item)) {
          if (item.length === 0) return null;
          return (
            <div
              key={`row-${index}`}
              className="flex flex-col gap-x-8 gap-y-6 md:flex-row"
            >
              {item.map((f, i) => (
                <div key={f.name} className="flex-1">
                  {renderFieldWithProps(f, i)}
                </div>
              ))}
            </div>
          );
        }
        return (
          <div key={item.name ?? `field-${index}`}>
            {renderFieldWithProps(item, index)}
          </div>
        );
      })}

      {jobSlug ? <input type="hidden" name="jobSlug" value={jobSlug} /> : null}

      {showPrivacyCheckbox && <PrivacyCheckbox />}
    </div>
  );
}
