"use client";

import { FormEvent, useEffect, useMemo, useRef, useState } from "react";

import { registration } from "../config/registration";
const steps = registration.steps;
const fileAccept = ".pdf,.jpg,.jpeg,.png,application/pdf,image/jpeg,image/png";
type Values = Record<string, string>;

function Field({
  label,
  name,
  type = "text",
  required = true,
  placeholder,
  help,
}: {
  label: string;
  name: string;
  type?: string;
  required?: boolean;
  placeholder?: string;
  help?: string;
}) {
  return (
    <label className="registration-field">
      <span>
        {label}
        {required && <b aria-hidden="true"> *</b>}
      </span>
      <input
        name={name}
        type={type}
        required={required}
        placeholder={placeholder}
        autoComplete="off"
        min={type === "number" ? "0" : undefined}
        step={type === "number" ? "0.01" : undefined}
      />
      {help && <small>{help}</small>}
    </label>
  );
}
function Upload({
  label,
  name,
  required = true,
}: {
  label: string;
  name: string;
  required?: boolean;
}) {
  return (
    <label className="registration-upload">
      <span>
        {label}
        {required && <b aria-hidden="true"> *</b>}
      </span>
      <input name={name} type="file" required={required} accept={fileAccept} />
      <small>PDF, JPG ou PNG · máximo 5 MB</small>
    </label>
  );
}
function Choice({
  label,
  name,
  options,
  required = true,
  onChange,
}: {
  label: string;
  name: string;
  options: string[];
  required?: boolean;
  onChange?: (value: string) => void;
}) {
  return (
    <fieldset className="registration-choice">
      <legend>
        {label}
        {required && <b aria-hidden="true"> *</b>}
      </legend>
      <div>
        {options.map((option) => (
          <label key={option}>
            <input
              type="radio"
              name={name}
              value={option}
              required={required}
              onChange={(event) => onChange?.(event.target.value)}
            />
            <span>{option}</span>
          </label>
        ))}
      </div>
    </fieldset>
  );
}

export function RegistrationForm() {
  const [step, setStep] = useState(0);
  const idempotencyKey = useRef<string | null>(null);
  const [values, setValues] = useState<Values>({
    currentStudent: "Não",
    siblings: "Não",
    socialSupport: "Não",
    guardianMode: "Pais",
    householdCount: "1",
    workArea: "Não",
  });
  const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">(
    "idle",
  );
  const [reference, setReference] = useState("");
  const [validationError, setValidationError] = useState("");
  const [submitError, setSubmitError] = useState("");
  const [draftToken, setDraftToken] = useState("");
  const [draftState, setDraftState] = useState<
    "idle" | "saving" | "saved" | "error"
  >("idle");
  const formRef = useRef<HTMLFormElement>(null);
  const update = (key: string, value: string) =>
    setValues((current) => ({ ...current, [key]: value }));
  const members = Math.min(10, Math.max(1, Number(values.householdCount) || 1));
  const total = useMemo(
    () =>
      Array.from({ length: members }, (_, i) =>
        Math.round(
          Number((values[`member.${i}.income`] || "0").replace(",", ".")) * 100,
        ),
      ).reduce((a, b) => a + b, 0) / 100,
    [members, values],
  );
  const guardians =
    values.guardianMode === "Pais"
      ? ["Pai", "Mãe"]
      : values.guardianMode === "Pai"
        ? ["Pai"]
        : values.guardianMode === "Mãe"
          ? ["Mãe"]
          : ["Outro responsável"];
  useEffect(() => {
    const token = new URLSearchParams(window.location.search).get("retomar");
    if (!token) return;
    fetch(`/api/application-drafts?token=${encodeURIComponent(token)}`)
      .then(async (response) => {
        if (!response.ok) throw new Error();
        return response.json() as Promise<{ values: Values; step: number }>;
      })
      .then((result) => {
        setDraftToken(token);
        setValues((current) => ({ ...current, ...result.values }));
        setStep(result.step);
        setTimeout(() => {
          for (const [name, fieldValue] of Object.entries(result.values)) {
            formRef.current
              ?.querySelectorAll<Element>(`[name="${CSS.escape(name)}"]`)
              .forEach((field) => {
                if (
                  field instanceof HTMLInputElement &&
                  (field.type === "radio" || field.type === "checkbox")
                )
                  field.checked = field.value === fieldValue;
                else if (
                  field instanceof HTMLInputElement ||
                  field instanceof HTMLSelectElement
                )
                  field.value = fieldValue;
              });
          }
        }, 0);
      })
      .catch(() => setDraftState("error"));
  }, []);
  async function saveDraft() {
    if (!formRef.current) return;
    setDraftState("saving");
    const saved: Values = { ...values };
    for (const [name, item] of new FormData(formRef.current).entries())
      if (typeof item === "string" && name !== "idempotencyKey")
        saved[name] = item;
    delete saved.truthConsent;
    delete saved.privacyConsent;
    try {
      const response = await fetch("/api/application-drafts", {
        method: draftToken ? "PUT" : "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          token: draftToken || undefined,
          values: saved,
          step,
        }),
      });
      const result = (await response.json()) as { token?: string };
      if (!response.ok) throw new Error();
      const token = result.token || draftToken;
      setDraftToken(token);
      window.history.replaceState(
        null,
        "",
        `${window.location.pathname}?retomar=${encodeURIComponent(token)}`,
      );
      setDraftState("saved");
    } catch {
      setDraftState("error");
    }
  }
  function validateStep() {
    setValidationError("");
    formRef.current
      ?.querySelectorAll(".field-invalid")
      .forEach((item) => item.classList.remove("field-invalid"));
    const panel = formRef.current?.querySelector<HTMLElement>(
      `[data-step="${step}"]`,
    );
    const fields = panel?.querySelectorAll("input,select,textarea");
    for (const node of fields || []) {
      if (
        !(
          node instanceof HTMLInputElement ||
          node instanceof HTMLSelectElement ||
          node instanceof HTMLTextAreaElement
        )
      )
        continue;
      const field = node;
      if (!field.checkValidity()) {
        const container = field.closest("label,fieldset");
        container?.classList.add("field-invalid");
        const raw =
          container?.querySelector("span,legend")?.textContent ||
          container?.textContent ||
          field.name ||
          "Campo obrigatório";
        const label = raw.replace("*", "").replace(/\s+/g, " ").trim();
        setValidationError(
          `Falta preencher: ${label}. O campo está assinalado abaixo.`,
        );
        setTimeout(() => {
          container?.scrollIntoView({ behavior: "smooth", block: "center" });
          field.focus({ preventScroll: true });
          field.reportValidity();
        }, 0);
        return false;
      }
    }
    return true;
  }
  function next() {
    if (validateStep()) {
      setStep((current) => Math.min(current + 1, steps.length - 1));
      window.scrollTo({ top: 0, behavior: "smooth" });
    }
  }
  async function submit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    if (!validateStep()) return;
    setStatus("sending");
    setSubmitError("");
    try {
      const body = new FormData(event.currentTarget);
      body.set("calculatedIncome", String(total));
      body.set(
        "idempotencyKey",
        (idempotencyKey.current ??= crypto.randomUUID()),
      );
      const response = await fetch("/api/applications", {
        method: "POST",
        body,
      });
      const result = (await response.json()) as {
        publicNumber?: string;
        error?: string;
      };
      if (!response.ok || !result.publicNumber) {
        const messages: Record<string, string> = {
          required_fields: "Existem campos obrigatórios por preencher.",
          invalid_email: "O endereço de email não é válido.",
          guardian_required: "Faltam dados obrigatórios de um responsável.",
          household_required:
            "Faltam dados de um elemento do agregado familiar.",
          required_documents:
            "Faltam documentos obrigatórios. Reveja os passos anteriores.",
          student_number_required: "Falta indicar o número de aluno.",
          siblings_required: "Faltam os números de aluno dos irmãos.",
          support_type_required: "Falta indicar o tipo de apoio social.",
          income_mismatch: "Reveja os rendimentos do agregado familiar.",
          rate_limited:
            "Foram efetuadas muitas tentativas. Aguarde alguns minutos.",
          invalid_fields: "Reveja datas, números, opções e consentimentos.",
          invalid_file: "Um ficheiro não é válido ou excede 5 MB.",
          invalid_signature:
            "Um ficheiro não corresponde ao formato PDF, JPG ou PNG.",
          upload_limit: "Foram enviados demasiados ficheiros ou mais de 50 MB.",
        };
        throw new Error(
          messages[result.error || ""] ||
            "Não foi possível concluir a inscrição. Tente novamente.",
        );
      }
      setReference(result.publicNumber);
      setStatus("sent");
      window.scrollTo({ top: 0, behavior: "smooth" });
    } catch (error) {
      setSubmitError(
        error instanceof Error
          ? error.message
          : "Não foi possível concluir a inscrição.",
      );
      setStatus("error");
    }
  }
  if (status === "sent")
    return (
      <section className="application-success">
        <span>✓</span>
        <p className="eyebrow">Inscrição recebida</p>
        <h2>Obrigado. A candidatura foi entregue.</h2>
        <p>
          Guarde o número <strong>{reference}</strong>. A receção não significa
          admissão; a equipa irá analisar os dados e documentos submetidos.
        </p>
        <a className="button" href="/">
          Voltar à página inicial
        </a>
      </section>
    );
  return (
    <form
      className="registration-form"
      ref={formRef}
      onSubmit={submit}
      noValidate
      onChange={(event) => {
        setValidationError("");
        const field = event.target as unknown as
          | HTMLInputElement
          | HTMLSelectElement;
        field.closest("label,fieldset")?.classList.remove("field-invalid");
        if (field.name && field.type !== "file")
          update(
            field.name,
            field instanceof HTMLInputElement && field.type === "checkbox"
              ? field.checked
                ? "on"
                : ""
              : field.value,
          );
      }}
    >
      {validationError && (
        <div className="validation-summary" role="alert" aria-live="assertive">
          <strong>Há informação por preencher</strong>
          <p>{validationError}</p>
        </div>
      )}
      <div className="draft-bar">
        <div>
          <strong>Quer continuar mais tarde?</strong>
          <span>
            Guarde os dados preenchidos. Os documentos terão de ser anexados
            quando retomar.
          </span>
        </div>
        <button
          type="button"
          onClick={saveDraft}
          disabled={draftState === "saving"}
        >
          {draftState === "saving"
            ? "A guardar…"
            : draftToken
              ? "Atualizar rascunho"
              : "Guardar rascunho"}
        </button>
        {draftState === "saved" && (
          <small>Rascunho guardado. Guarde esta ligação privada.</small>
        )}
        {draftState === "error" && (
          <small className="draft-error">
            Não foi possível guardar ou retomar o rascunho.
          </small>
        )}
      </div>
      <div className="registration-progress">
        <div className="progress-top">
          <span>
            Passo {step + 1} de {steps.length}
          </span>
          <strong>{steps[step]}</strong>
        </div>
        <ol>
          {steps.map((label, index) => (
            <li
              className={index === step ? "active" : index < step ? "done" : ""}
              key={label}
            >
              <button
                type="button"
                onClick={() => {
                  if (index < step) setStep(index);
                }}
              >
                <span>{index < step ? "✓" : index + 1}</span>
                {label}
              </button>
            </li>
          ))}
        </ol>
      </div>
      <div className="registration-panel" data-step="0" hidden={step !== 0}>
        <p className="eyebrow">Antes de começar</p>
        <h2>Prepare a documentação.</h2>
        <p>{registration.preparation}</p>
        <div className="preparation-grid">
          <div>
            <strong>Identificação</strong>
            <p>Cartões de Cidadão da criança e responsáveis, NIF e NISS.</p>
          </div>
          <div>
            <strong>Saúde</strong>
            <p>Comprovativo do plano de vacinação adequado à idade.</p>
          </div>
          <div>
            <strong>Rendimentos</strong>
            <p>
              Dois comprovativos por membro do agregado e declaração de IRS.
            </p>
          </div>
          <div>
            <strong>Despesas</strong>
            <p>Habitação, transporte e saúde, quando aplicável.</p>
          </div>
        </div>
        <label className="consent-check">
          <input name="informationConfirmed" type="checkbox" required /> Li a
          informação e tenho os documentos necessários.
        </label>
      </div>
      <div className="registration-panel" data-step="1" hidden={step !== 1}>
        <p className="eyebrow">Dados da criança</p>
        <h2>Identificação e resposta pretendida.</h2>
        <Choice
          label={registration.currentStudentLabel}
          name="currentStudent"
          options={["Sim", "Não"]}
          onChange={(v) => update("currentStudent", v)}
        />
        {values.currentStudent === "Sim" && (
          <Field label="Número de aluno" name="studentNumber" />
        )}
        <div className="form-row">
          <Field label="Nome completo" name="childName" />
          <Field label="Data de nascimento" name="birthDate" type="date" />
        </div>
        <label className="registration-field">
          <span>Resposta social pretendida *</span>
          <select name="socialResponse" required defaultValue="">
            <option value="" disabled>
              Selecione
            </option>
            {registration.responses.map((response) => (
              <option key={response.code} value={response.code}>
                {response.name}
              </option>
            ))}
          </select>
        </label>
        <Field label="Morada" name="childAddress" />
        <div className="form-row">
          <Field
            label="Código postal"
            name="childPostalCode"
            placeholder="0000-000"
          />
          <Field label="Localidade" name="childLocality" />
        </div>
        <div className="form-row">
          <Field label="Cartão de Cidadão" name="childCitizenCard" />
          <Field label="Validade" name="childCitizenCardExpiry" type="date" />
        </div>
        <div className="form-row">
          <Field label="NIF" name="childTaxNumber" />
          <Field label="NISS" name="childSocialSecurity" />
        </div>
        <div className="upload-grid">
          <Upload
            label="Cartão de Cidadão da criança"
            name="doc.child_cc"
            required={registration.requiredDocuments.includes("child_cc")}
          />
          <Upload
            label="Plano de vacinação"
            name="doc.vaccination"
            required={registration.requiredDocuments.includes("vaccination")}
          />
        </div>
        <Choice
          label="Tem irmãos a frequentar a instituição?"
          name="siblings"
          options={["Sim", "Não"]}
          onChange={(v) => update("siblings", v)}
        />
        {values.siblings === "Sim" && (
          <Field
            label="Números de aluno dos irmãos"
            name="siblingNumbers"
            help="Separe vários números com vírgulas."
          />
        )}
        <Choice
          label="Recebe apoio social?"
          name="socialSupport"
          options={["Sim", "Não"]}
          onChange={(v) => update("socialSupport", v)}
        />
        {values.socialSupport === "Sim" && (
          <>
            <Field label="Tipo de apoio" name="socialSupportType" />
            <Upload
              label="Comprovativo de apoio social"
              name="doc.social_support"
            />
          </>
        )}
      </div>
      <div className="registration-panel" data-step="2" hidden={step !== 2}>
        <p className="eyebrow">Responsáveis</p>
        <h2>Filiação e encarregado de educação.</h2>
        <Choice
          label="A criança está a cargo de"
          name="guardianMode"
          options={["Pais", "Pai", "Mãe", "Outro responsável"]}
          onChange={(v) => update("guardianMode", v)}
        />
        {guardians.map((guardian, index) => (
          <section className="guardian-block" key={guardian}>
            <div className="block-heading">
              <span>{String(index + 1).padStart(2, "0")}</span>
              <h3>{guardian}</h3>
            </div>
            <input
              type="hidden"
              name={`guardian.${index}.role`}
              value={guardian}
            />
            {guardian === "Outro responsável" && (
              <Field
                label="Parentesco"
                name={`guardian.${index}.relationship`}
              />
            )}
            <Field label="Nome completo" name={`guardian.${index}.name`} />
            <Field label="Morada" name={`guardian.${index}.address`} />
            <div className="form-row">
              <Field
                label="Código postal"
                name={`guardian.${index}.postalCode`}
              />
              <Field label="Localidade" name={`guardian.${index}.locality`} />
            </div>
            <div className="form-row">
              <Field
                label="Cartão de Cidadão"
                name={`guardian.${index}.citizenCard`}
              />
              <Field
                label="Validade"
                name={`guardian.${index}.citizenCardExpiry`}
                type="date"
              />
            </div>
            <div className="form-row">
              <Field label="NIF" name={`guardian.${index}.taxNumber`} />
              <Field
                label="Telefone"
                name={`guardian.${index}.phone`}
                type="tel"
              />
            </div>
            <Field
              label="Email"
              name={`guardian.${index}.email`}
              type="email"
            />
            <Upload
              label={`Cartão de Cidadão — ${guardian}`}
              name={`doc.guardian_${index}_cc`}
            />
          </section>
        ))}
        {values.guardianMode !== "Pais" && (
          <Upload
            label="Regulação das responsabilidades parentais"
            name="doc.parental_responsibility"
          />
        )}
      </div>
      <div className="registration-panel" data-step="3" hidden={step !== 3}>
        <p className="eyebrow">Situação económica</p>
        <h2>Agregado, rendimentos e despesas.</h2>
        <label className="registration-field compact">
          <span>Número de elementos do agregado *</span>
          <select
            name="householdCount"
            value={values.householdCount}
            onChange={(event) => update("householdCount", event.target.value)}
          >
            {Array.from({ length: 10 }, (_, i) => (
              <option key={i + 1}>{i + 1}</option>
            ))}
          </select>
        </label>
        {Array.from({ length: members }, (_, index) => (
          <section className="household-block" key={index}>
            <div className="block-heading">
              <span>{String(index + 1).padStart(2, "0")}</span>
              <h3>Elemento do agregado</h3>
            </div>
            <div className="form-row">
              <Field label="Nome completo" name={`member.${index}.name`} />
              <Field label="Parentesco" name={`member.${index}.relationship`} />
            </div>
            <Field
              label="Rendimento mensal líquido (€)"
              name={`member.${index}.income`}
              type="number"
              required={false}
            />
            {Number(values[`member.${index}.income`] || 0) > 0 && (
              <div className="upload-grid">
                <Upload
                  label="Comprovativo de rendimento 1"
                  name={`doc.member_${index}_income_1`}
                />
                <Upload
                  label="Comprovativo de rendimento 2"
                  name={`doc.member_${index}_income_2`}
                />
              </div>
            )}
          </section>
        ))}
        <div className="income-total">
          <span>Rendimento mensal total calculado</span>
          <strong>
            {total.toLocaleString("pt-PT", {
              style: "currency",
              currency: "EUR",
            })}
          </strong>
        </div>
        <Choice
          label="O encarregado trabalha na área de influência da instituição?"
          name="workArea"
          options={["Sim", "Não"]}
          onChange={(v) => update("workArea", v)}
        />
        {values.workArea === "Sim" && (
          <Upload
            label="Comprovativo da atividade profissional"
            name="doc.work_area"
          />
        )}
        <Upload
          label="Declaração de IRS"
          name="doc.irs"
          required={registration.requiredDocuments.includes("irs")}
        />
        <h3 className="subsection-title">Despesas mensais</h3>
        {[
          ["Habitação", "housing"],
          ["Transportes", "transport"],
          ["Saúde", "health"],
        ].map(([label, key]) => (
          <div className="expense-row" key={key}>
            <Field
              label={`${label} (€)`}
              name={`expense.${key}`}
              type="number"
              required={false}
            />
            {Number(values[`expense.${key}`] || 0) > 0 && (
              <Upload
                label={`Comprovativo — ${label.toLowerCase()}`}
                name={`doc.expense_${key}`}
              />
            )}
          </div>
        ))}
      </div>
      <div className="registration-panel" data-step="4" hidden={step !== 4}>
        <p className="eyebrow">Confirmação</p>
        <h2>Reveja antes de enviar.</h2>
        <div className="review-grid">
          <div>
            <span>Criança</span>
            <strong>{values.childName || "—"}</strong>
            <p>
              {registration.responses.find(
                (response) => response.code === values.socialResponse,
              )?.name || "Resposta por indicar"}
            </p>
          </div>
          <div>
            <span>Responsáveis</span>
            <strong>{values.guardianMode}</strong>
            <p>{guardians.length} responsável/eis indicado/s</p>
          </div>
          <div>
            <span>Agregado</span>
            <strong>{members} elemento/s</strong>
            <p>
              {total.toLocaleString("pt-PT", {
                style: "currency",
                currency: "EUR",
              })}{" "}
              mensais
            </p>
          </div>
        </div>
        <Field
          label="Email para receber o comprovativo"
          name="confirmationEmail"
          type="email"
        />
        <label className="consent-check">
          <input name="truthConsent" type="checkbox" required /> Declaro que os
          dados fornecidos são verdadeiros e completos.
        </label>
        <label className="consent-check">
          <input name="privacyConsent" type="checkbox" required />
          <span>
            Li a{" "}
            <a href="/privacidade" target="_blank" rel="noreferrer">
              Política de Privacidade
            </a>{" "}
            e autorizo o tratamento dos dados para gestão desta candidatura.
          </span>
        </label>
        <div className="submission-notice">
          <strong>A receção não significa admissão.</strong>
          <p>
            A candidatura só fica entregue depois de receber o número de
            confirmação.
          </p>
        </div>
        {status === "error" && (
          <p className="form-error" role="alert">
            {submitError ||
              "Não foi possível concluir a inscrição. Tente novamente; os campos preenchidos permanecem nesta página."}
          </p>
        )}
      </div>
      <div className="registration-actions">
        {step > 0 && (
          <button
            type="button"
            className="secondary-button"
            onClick={() => setStep((current) => current - 1)}
          >
            ← Anterior
          </button>
        )}
        <span></span>
        {step < steps.length - 1 ? (
          <button type="button" className="button" onClick={next}>
            Continuar →
          </button>
        ) : (
          <button
            type="submit"
            className="button"
            disabled={status === "sending"}
          >
            {status === "sending"
              ? "A enviar candidatura…"
              : "Enviar candidatura"}
          </button>
        )}
      </div>
    </form>
  );
}
