"use client";

import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import { Button, Dialog, Field, Input, Select } from "@/components/ui/kit";
import { useToast } from "@/components/ui/toast";
import { api, ApiError } from "@/lib/api";
import { useApi } from "@/lib/use-api";
import type { Binding, CustomField, MessageTemplate } from "@/lib/types";
import { componentsToBubble, PreviewFrame, TemplateBubble } from "./template-preview";

const CONTACT_FIELDS: [string, string][] = [
  ["first_name", "First name"],
  ["last_name", "Last name"],
  ["full_name", "Full name"],
  ["phone", "WhatsApp number"],
  ["email", "Email"],
  ["country", "Country"],
  ["language_code", "Language code"],
];

export type Slot = { group: "header" | "body" | "buttons"; key: string; label: string };
type Bindings = { header: Record<string, Binding>; body: Record<string, Binding>; buttons: Record<string, Binding>; header_media_link?: string };

export function defaultBinding(v: string): Binding {
  // A sensible first guess so the common case is one click.
  if (/^(1|name|first_name|customer_name)$/.test(v)) return { source: "contact", field: "first_name", fallback: "there" };
  return { source: "text", value: "" };
}

export function BindingEditor({ slot, value, onChange, customFields, error }: { slot: Slot; value: Binding; onChange: (b: Binding) => void; customFields: CustomField[]; error?: string }) {
  const id = `b-${slot.group}-${slot.key}`;
  const selectValue = value.source === "text" ? "__text" : value.field;
  return (
    <Field label={slot.label} htmlFor={id} error={error}>
      <div className="grid gap-2 sm:grid-cols-2">
        <Select
          id={id}
          value={selectValue}
          onChange={(e) => (e.target.value === "__text" ? onChange({ source: "text", value: "" }) : onChange({ source: "contact", field: e.target.value, fallback: value.source === "contact" ? value.fallback : "" }))}
        >
          <option value="__text">Type a value</option>
          <optgroup label="Contact details">
            {CONTACT_FIELDS.map(([k, l]) => (
              <option key={k} value={k}>
                {l}
              </option>
            ))}
          </optgroup>
          {customFields.length > 0 && (
            <optgroup label="Custom fields">
              {customFields.map((f) => (
                <option key={f.key} value={`custom:${f.key}`}>
                  {f.label}
                </option>
              ))}
            </optgroup>
          )}
        </Select>
        {value.source === "text" ? (
          <Input aria-label={`${slot.label} value`} value={value.value} onChange={(e) => onChange({ source: "text", value: e.target.value })} invalid={!!error} />
        ) : (
          <Input aria-label={`${slot.label} fallback`} placeholder="If empty, use…" value={value.fallback ?? ""} onChange={(e) => onChange({ ...value, fallback: e.target.value })} />
        )}
      </div>
    </Field>
  );
}

/**
 * Pick an approved template, map its variables to contact details or text, preview, send.
 * With `conversationId` it sends inside that conversation; otherwise to the contact's default number.
 */
export function SendTemplateDialog({ contactId, contactName, conversationId, onClose, onSent }: { contactId: number; contactName: string; conversationId?: number; onClose: () => void; onSent: () => void }) {
  const toast = useToast();
  const templatesReq = useApi<{ data: MessageTemplate[] }>("/templates?status=APPROVED");
  const fieldsReq = useApi<{ data: CustomField[] }>("/custom-fields");
  const templates = useMemo(() => templatesReq.data?.data ?? [], [templatesReq.data]);
  const customFields = fieldsReq.data?.data ?? [];
  const [templateId, setTemplateId] = useState<number | null>(null);
  const [bindings, setBindings] = useState<Bindings>({ header: {}, body: {}, buttons: {} });
  const [preview, setPreview] = useState<{ header: string | null; body: string; footer: string | null; buttons: string[] } | null>(null);
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [sending, setSending] = useState(false);
  const template = templates.find((t) => t.id === templateId) ?? null;

  const slots: Slot[] = useMemo(() => {
    if (!template) return [];
    const v = template.variables;
    return [
      ...v.header.variables.map((k) => ({ group: "header" as const, key: k, label: `Header {{${k}}}` })),
      ...v.body.map((k) => ({ group: "body" as const, key: k, label: `{{${k}}}` })),
      ...v.buttons.filter((b) => b.variable).map((b) => ({ group: "buttons" as const, key: String(b.index), label: `“${b.text}” link ending` })),
    ];
  }, [template]);

  function choose(id: number) {
    const t = templates.find((x) => x.id === id);
    setTemplateId(id);
    setErrors({});
    setPreview(null);
    if (!t) return;
    const b: Bindings = { header: {}, body: {}, buttons: {} };
    t.variables.header.variables.forEach((k) => (b.header[k] = { source: "text", value: "" }));
    t.variables.body.forEach((k) => (b.body[k] = defaultBinding(k)));
    t.variables.buttons.filter((x) => x.variable).forEach((x) => (b.buttons[String(x.index)] = { source: "text", value: "" }));
    setBindings(b);
  }

  // Server-rendered preview with the real contact data (debounced; state is set in the timer callback).
  useEffect(() => {
    if (!templateId) return;
    const t = setTimeout(async () => {
      try {
        const r = await api.post<{ data: NonNullable<typeof preview> }>(`/templates/${templateId}/preview`, { contact_id: contactId, bindings });
        setPreview(r.data);
        setErrors({});
      } catch (err) {
        setPreview(null);
        if (err instanceof ApiError) setErrors(err.fields);
      }
    }, 300);
    return () => clearTimeout(t);
  }, [templateId, bindings, contactId]);

  async function send() {
    if (!template) return;
    setSending(true);
    try {
      if (conversationId) await api.post(`/inbox/conversations/${conversationId}/template`, { template_id: template.id, bindings });
      else await api.post(`/templates/${template.id}/send`, { contact_id: contactId, bindings });
      toast("success", `Sent ${template.name} to ${contactName}`);
      onSent();
    } catch (err) {
      if (err instanceof ApiError && Object.keys(err.fields).length) setErrors(err.fields);
      else toast("error", (err as Error).message);
    } finally {
      setSending(false);
    }
  }

  const fallback = template ? componentsToBubble(template.components) : null;
  const errorFor = (s: Slot) => errors[`${s.group}.${s.key}`];

  return (
    <Dialog
      open
      onClose={onClose}
      size="lg"
      title="Send a template message"
      description={`To ${contactName}. Templates can be sent anytime, even outside the 24-hour reply window.`}
      footer={
        <>
          <Button onClick={onClose}>Cancel</Button>
          <Button variant="primary" disabled={!template || !preview} loading={sending} onClick={send}>
            Send template
          </Button>
        </>
      }
    >
      {templatesReq.data && templates.length === 0 ? (
        <p className="text-sm text-muted">
          No approved templates yet.{" "}
          <Link href="/templates" className="font-medium text-teal hover:underline">
            Create or sync templates
          </Link>{" "}
          first.
        </p>
      ) : (
        <div className="grid gap-6 md:grid-cols-[minmax(0,1fr)_300px]">
          <div className="space-y-5">
            <Field label="Template" htmlFor="st-template" error={errors.template_id}>
              <Select id="st-template" value={templateId ?? ""} onChange={(e) => choose(Number(e.target.value))} invalid={!!errors.template_id}>
                <option value="" disabled>
                  Choose an approved template
                </option>
                {templates.map((t) => (
                  <option key={t.id} value={t.id}>
                    {t.name} ({t.language}, {t.category.toLowerCase()})
                  </option>
                ))}
              </Select>
            </Field>
            {template?.variables.header.media && (
              <Field label={`Header ${template.variables.header.format?.toLowerCase()}`} htmlFor="st-media" error={errors.header_media_link} hint="A public https link to the file.">
                <Input id="st-media" value={bindings.header_media_link ?? ""} onChange={(e) => setBindings((b) => ({ ...b, header_media_link: e.target.value || undefined }))} invalid={!!errors.header_media_link} />
              </Field>
            )}
            {slots.length > 0 && <p className="text-[13px] text-muted">Fill each variable from the contact&apos;s details or type a value.</p>}
            {slots.map((s) => (
              <BindingEditor
                key={`${s.group}-${s.key}`}
                slot={s}
                customFields={customFields}
                value={bindings[s.group][s.key] ?? { source: "text", value: "" }}
                error={errorFor(s)}
                onChange={(b) => setBindings((cur) => ({ ...cur, [s.group]: { ...cur[s.group], [s.key]: b } }))}
              />
            ))}
          </div>
          <aside>
            <p className="mb-2 text-[13px] font-medium text-muted">What {contactName.split(" ")[0]} will see</p>
            <PreviewFrame>
              {template ? (
                preview ? (
                  <TemplateBubble header={preview.header} body={preview.body} footer={preview.footer} buttons={fallback!.buttons} />
                ) : (
                  <div className="opacity-60">
                    <TemplateBubble {...fallback!} />
                  </div>
                )
              ) : (
                <p className="py-8 text-center text-sm text-[#667781]">Choose a template</p>
              )}
            </PreviewFrame>
          </aside>
        </div>
      )}
    </Dialog>
  );
}
