"use client";

import { useEffect, useMemo, useState, type FormEvent } from "react";
import { X } from "lucide-react";
import { Button, Dialog, Field, Input, Select } from "@/components/ui/kit";
import { useToast } from "@/components/ui/toast";
import { api, ApiError, qs } from "@/lib/api";
import { countries } from "@/lib/format";
import type { Contact, CustomField, Group, OptInStatus } from "@/lib/types";

interface FormState {
  first_name: string;
  last_name: string;
  phone: string;
  email: string;
  country: string;
  language_code: string;
  opt_in_status: OptInStatus;
  consent_source: string;
  group_ids: number[];
  tags: string[];
  custom_fields: Record<string, string>;
}

const empty: FormState = {
  first_name: "",
  last_name: "",
  phone: "",
  email: "",
  country: "",
  language_code: "",
  opt_in_status: "unknown",
  consent_source: "",
  group_ids: [],
  tags: [],
  custom_fields: {},
};

function fromContact(c: Contact): FormState {
  return {
    first_name: c.first_name,
    last_name: c.last_name ?? "",
    phone: c.phone,
    email: c.email ?? "",
    country: c.country ?? "",
    language_code: c.language_code ?? "",
    opt_in_status: c.opt_in_status,
    consent_source: c.consent_source ?? "",
    group_ids: c.groups.map((g) => g.id),
    tags: c.tags.map((t) => t.name),
    custom_fields: { ...c.custom_fields },
  };
}

export function ContactForm({
  open,
  contact,
  groups,
  customFields,
  onClose,
  onSaved,
  onOpenExisting,
}: {
  open: boolean;
  contact: Contact | null;
  groups: Group[];
  customFields: CustomField[];
  onClose: () => void;
  onSaved: (c: Contact) => void;
  onOpenExisting: (id: number) => void;
}) {
  const toast = useToast();
  // The parent mounts this component with a key per contact, so initial state comes from props.
  const [form, setForm] = useState<FormState>(() => (contact ? fromContact(contact) : empty));
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [saving, setSaving] = useState(false);
  const [tagDraft, setTagDraft] = useState("");
  const [dupCheck, setDupCheck] = useState<{ phone: string; match: { id: number; name: string } | null } | null>(null);
  const countryList = useMemo(() => countries(), []);

  const phoneDigits = form.phone.replace(/\D/g, "");
  const duplicate = phoneDigits.length >= 8 && dupCheck?.phone === form.phone ? dupCheck.match : null;

  // Live duplicate check on the phone number (debounced). State is only set in the async callback.
  useEffect(() => {
    if (form.phone.replace(/\D/g, "").length < 8) return;
    const phone = form.phone;
    const t = setTimeout(async () => {
      try {
        const r = await api.get<{ phone: { id: number; first_name: string; last_name: string | null } | null }>(
          `/contacts/check-duplicate${qs({ phone, except_id: contact?.id })}`,
        );
        setDupCheck({ phone, match: r.phone ? { id: r.phone.id, name: [r.phone.first_name, r.phone.last_name].filter(Boolean).join(" ") } : null });
      } catch {
        /* advisory only */
      }
    }, 350);
    return () => clearTimeout(t);
  }, [form.phone, contact?.id]);

  const set = <K extends keyof FormState>(key: K, value: FormState[K]) => {
    setForm((f) => ({ ...f, [key]: value }));
    // Clear a field's error as soon as the user edits it.
    setErrors((e) => {
      if (!Object.keys(e).some((k) => k === key || k.startsWith(`${key}.`))) return e;
      return Object.fromEntries(Object.entries(e).filter(([k]) => k !== key && !k.startsWith(`${key}.`)));
    });
  };

  function addTag() {
    const t = tagDraft.trim().replace(/,$/, "");
    if (t && !form.tags.some((x) => x.toLowerCase() === t.toLowerCase())) set("tags", [...form.tags, t]);
    setTagDraft("");
  }

  async function onSubmit(e: FormEvent) {
    e.preventDefault();
    setSaving(true);
    setErrors({});
    const payload = { ...form, tags: tagDraft.trim() ? [...form.tags, tagDraft.trim()] : form.tags };
    try {
      const res = contact ? await api.patch<{ data: Contact }>(`/contacts/${contact.id}`, payload) : await api.post<{ data: Contact }>("/contacts", payload);
      toast("success", contact ? "Contact saved" : "Contact added");
      onSaved(res.data);
    } catch (err) {
      if (err instanceof ApiError) {
        if (err.code === "duplicate_phone") {
          setErrors({ phone: err.message });
          setDupCheck({ phone: form.phone, match: { id: Number(err.details?.existing_id), name: String(err.details?.existing_name ?? "") } });
        } else if (Object.keys(err.fields).length) setErrors(err.fields);
        else toast("error", err.message);
      } else toast("error", "Could not save. Check your connection and try again.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <Dialog
      open={open}
      onClose={onClose}
      title={contact ? "Edit contact" : "Add contact"}
      size="lg"
      footer={
        <>
          <Button onClick={onClose}>Cancel</Button>
          <Button variant="primary" type="submit" form="contact-form" loading={saving}>
            {contact ? "Save contact" : "Add contact"}
          </Button>
        </>
      }
    >
      <form id="contact-form" onSubmit={onSubmit} noValidate className="space-y-6">
        <div className="grid gap-4 sm:grid-cols-2">
          <Field label="First name" htmlFor="cf-first" error={errors.first_name}>
            <Input id="cf-first" value={form.first_name} onChange={(e) => set("first_name", e.target.value)} invalid={!!errors.first_name} autoFocus />
          </Field>
          <Field label="Last name" htmlFor="cf-last" optional error={errors.last_name}>
            <Input id="cf-last" value={form.last_name} onChange={(e) => set("last_name", e.target.value)} />
          </Field>
          <Field
            label="WhatsApp number"
            htmlFor="cf-phone"
            error={errors.phone}
            hint={
              duplicate ? (
                <span className="text-amber">
                  Already saved as {duplicate.name || "another contact"}.{" "}
                  <button type="button" className="font-medium underline" onClick={() => onOpenExisting(duplicate.id)}>
                    Open it
                  </button>
                </span>
              ) : (
                "Include the country code, without 0 or +. Example: 919812345670"
              )
            }
          >
            <Input id="cf-phone" inputMode="tel" className="tabular" value={form.phone} onChange={(e) => set("phone", e.target.value)} invalid={!!errors.phone} />
          </Field>
          <Field label="Email" htmlFor="cf-email" optional error={errors.email}>
            <Input id="cf-email" type="email" value={form.email} onChange={(e) => set("email", e.target.value)} invalid={!!errors.email} />
          </Field>
          <Field label="Country" htmlFor="cf-country" optional error={errors.country}>
            <Select id="cf-country" value={form.country} onChange={(e) => set("country", e.target.value)}>
              <option value="">Not set</option>
              {countryList.map((c) => (
                <option key={c.code} value={c.code}>
                  {c.name}
                </option>
              ))}
            </Select>
          </Field>
          <Field label="Language code" htmlFor="cf-lang" optional error={errors.language_code} hint="Matches template languages, e.g. en, hi, en_US">
            <Input id="cf-lang" value={form.language_code} onChange={(e) => set("language_code", e.target.value)} invalid={!!errors.language_code} />
          </Field>
        </div>

        <fieldset className="space-y-3">
          <legend className="text-[13px] font-medium">Groups</legend>
          {groups.length ? (
            <div className="flex flex-wrap gap-2">
              {groups.map((g) => {
                const on = form.group_ids.includes(g.id);
                return (
                  <label
                    key={g.id}
                    className={`cursor-pointer rounded-full border px-3 py-1 text-sm transition-colors has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-mint ${on ? "border-teal bg-teal-soft text-teal" : "border-line text-muted hover:border-teal/40"}`}
                  >
                    <input
                      type="checkbox"
                      className="sr-only"
                      checked={on}
                      onChange={() => set("group_ids", on ? form.group_ids.filter((x) => x !== g.id) : [...form.group_ids, g.id])}
                    />
                    {g.name}
                  </label>
                );
              })}
            </div>
          ) : (
            <p className="text-sm text-muted">No groups yet. Create them on the Groups page.</p>
          )}
          {errors.group_ids && <p className="text-[13px] text-brick">{errors.group_ids}</p>}
        </fieldset>

        <Field label="Tags" htmlFor="cf-tags" optional hint="Press Enter or comma to add a tag.">
          <div className="flex min-h-10 flex-wrap items-center gap-1.5 rounded-md border border-line bg-white px-2 py-1.5 focus-within:border-teal focus-within:ring-2 focus-within:ring-teal/15">
            {form.tags.map((t) => (
              <span key={t} className="inline-flex items-center gap-1 rounded bg-paper px-2 py-0.5 text-sm">
                {t}
                <button type="button" aria-label={`Remove ${t}`} onClick={() => set("tags", form.tags.filter((x) => x !== t))} className="text-muted hover:text-brick">
                  <X className="size-3.5" />
                </button>
              </span>
            ))}
            <input
              id="cf-tags"
              value={tagDraft}
              onChange={(e) => setTagDraft(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === "Enter" || e.key === ",") {
                  e.preventDefault();
                  addTag();
                } else if (e.key === "Backspace" && !tagDraft && form.tags.length) set("tags", form.tags.slice(0, -1));
              }}
              onBlur={addTag}
              className="min-w-24 flex-1 bg-transparent px-1 text-sm focus:outline-none"
            />
          </div>
        </Field>

        <div className="grid gap-4 sm:grid-cols-2">
          <Field label="Marketing consent" htmlFor="cf-consent" hint="Opted-out contacts never receive campaigns.">
            <Select id="cf-consent" value={form.opt_in_status} onChange={(e) => set("opt_in_status", e.target.value as OptInStatus)}>
              <option value="unknown">Unknown</option>
              <option value="opted_in">Opted in</option>
              <option value="opted_out">Opted out</option>
            </Select>
          </Field>
          <Field label="How consent was given" htmlFor="cf-source" optional error={errors.consent_source}>
            <Input id="cf-source" placeholder="e.g. Website form, in store" value={form.consent_source} onChange={(e) => set("consent_source", e.target.value)} />
          </Field>
        </div>

        {customFields.length > 0 && (
          <fieldset>
            <legend className="mb-3 text-[13px] font-medium">Other information</legend>
            <div className="grid gap-4 sm:grid-cols-2">
              {customFields.map((f) => (
                <Field key={f.id} label={f.label} htmlFor={`cf-x-${f.key}`} optional error={errors[`custom_fields.${f.key}`]}>
                  <Input
                    id={`cf-x-${f.key}`}
                    type={f.type === "number" ? "number" : f.type === "date" ? "date" : f.type === "email" ? "email" : "text"}
                    value={form.custom_fields[f.key] ?? ""}
                    onChange={(e) => set("custom_fields", { ...form.custom_fields, [f.key]: e.target.value })}
                    invalid={!!errors[`custom_fields.${f.key}`]}
                  />
                </Field>
              ))}
            </div>
          </fieldset>
        )}
      </form>
    </Dialog>
  );
}
