// Shared email composer used by BOTH the step's original email and each A/B
// variant, so every arm has the identical toolset: a template picker, save as
// template, Write with AI, a subject with variable insert, a body with an
// Edit/Preview toggle that renders through the real send engine (for a chosen
// contact and mailbox, with signature, opt-out footer and attachments when the
// campaign is known), a "send test" action, template validity warnings, and
// the advisory content score.

import React from "react";
import {
    AlertCircleIcon,
    BookmarkPlusIcon,
    EyeIcon,
    Loader2Icon,
    PaperclipIcon,
    PencilLineIcon,
    SparklesIcon,
} from "lucide-react";
import toast from "react-hot-toast";
import RichTextEditor, { VariableMenu } from "./RichTextEditor";
import { useTemplatePreview } from "@/lib/api/hooks/app/campaigns/useTemplatePreview";
import type { TemplatePreview } from "@/lib/api/client/app/campaigns/previewTemplate";
import type Contact from "@/lib/api/models/app/contacts/Contact";
import type Inbox from "@/lib/api/models/app/emails/Inbox";
import formatBytes from "@/lib/helper/formatBytes";
import { PreviewContactPicker, PreviewMailboxPicker, SendTestButton } from "./PreviewControls";
import { SAMPLE_CONTACT_LABEL, contactLabel, useCampaignSenderInboxes } from "./previewContext";
import { Label, TextInput } from "@/components/ui/field";
import {
    PopoverMenu,
    PopoverMenuContent,
    PopoverMenuTrigger,
    SelectButton,
} from "@/components/ui/popover-menu";
import ContentScore from "../ContentScore";
import useTemplates from "@/lib/api/hooks/app/templates/useTemplates";
import useCreateTemplate from "@/lib/api/hooks/app/templates/useCreateTemplate";
import { useConfirm } from "@/hooks/context/confirm";
import type { AppError } from "@/lib/api/client/normalizeError";
import buildError from "@/lib/helper/buildError";
import { VARIABLES, htmlToPlain, promptToHtml, renderPreview, templateIssue } from "./emailPreview";
import { LINK_VARIABLES } from "@/lib/templateVars";

export default function EmailContentEditor({
    subject,
    onSubjectChange,
    bodyHtml,
    onBodyChange,
    subjectPlaceholder = "Quick question, {{.FirstName}}",
    bodyPlaceholder = "Hi {{.FirstName}}, …",
    campaignId,
    stepId,
    canSendTest = false,
    dirty = false,
}: {
    subject: string;
    onSubjectChange: (value: string) => void;
    bodyHtml: string;
    onBodyChange: (html: string, plain: string) => void;
    subjectPlaceholder?: string;
    bodyPlaceholder?: string;
    // When set, the preview renders for a chosen lead and mailbox and shows the
    // campaign's opt-out footer, signature and attachments.
    campaignId?: string;
    // The saved step this copy is sent by, so the preview lists the files that
    // step attaches.
    stepId?: string;
    // Offer "Send test" for that step. Off for a variant arm: the test send
    // mails the step's saved copy, which is not what this arm holds.
    canSendTest?: boolean;
    // Unsaved edits: the test send mails the saved step, so it waits for a save.
    dirty?: boolean;
}) {
    const [tab, setTab] = React.useState<"edit" | "preview">("edit");

    // Preview context: null contact = the built-in sample; the mailbox defaults
    // to the campaign's first enabled sender once the pool has loaded.
    const [previewContact, setPreviewContact] = React.useState<Contact | null>(null);
    const [previewMailbox, setPreviewMailbox] = React.useState<Inbox | null>(null);
    const [serverPreview, setServerPreview] = React.useState<TemplatePreview | null>(null);
    const senders = useCampaignSenderInboxes(campaignId ?? "");
    // Both picks belong to one campaign, so drop them when this editor is
    // pointed at a different one, or at none, without remounting. The rendered
    // panel goes with them: it names a sender and files of the old campaign.
    React.useEffect(() => {
        setPreviewContact(null);
        setPreviewMailbox(null);
        setServerPreview(null);
    }, [campaignId]);
    React.useEffect(() => {
        if (!campaignId) return;
        if (previewMailbox && senders.inboxes.some((i) => i.id === previewMailbox.id)) return;
        setPreviewMailbox(senders.inboxes[0] ?? null);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [campaignId, senders.inboxes.map((i) => i.id).join(",")]);

    const previewMut = useTemplatePreview();
    const runPreview = previewMut.mutateAsync;
    React.useEffect(() => {
        if (tab !== "preview") return;
        // Responses can land out of order; only the newest request may paint.
        let active = true;
        const t = setTimeout(() => {
            runPreview({
                subject,
                body_html: bodyHtml,
                body_plain: htmlToPlain(bodyHtml),
                ...(campaignId && previewContact ? { contact_id: previewContact.id } : {}),
                ...(campaignId ? { campaign_id: campaignId } : {}),
                ...(campaignId && previewMailbox ? { account_id: previewMailbox.id } : {}),
                ...(campaignId && stepId ? { step_id: stepId } : {}),
            })
                .then((p) => {
                    if (active) setServerPreview(p);
                })
                .catch(() => {
                    if (active) setServerPreview(null);
                });
        }, 250);
        return () => {
            active = false;
            clearTimeout(t);
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [tab, subject, bodyHtml, previewContact?.id, campaignId, previewMailbox?.id, stepId]);

    const tplIssue = templateIssue(subject) || templateIssue(bodyHtml) || templateIssue(htmlToPlain(bodyHtml));

    // Toolbar: template library, save as template, write with AI.
    const { data: templates } = useTemplates("");
    const createTemplate = useCreateTemplate();
    const confirm = useConfirm();
    const [saveTplOpen, setSaveTplOpen] = React.useState(false);
    const [tplName, setTplName] = React.useState("");

    function applyTemplate(t: { name: string; subject: string; body_html: string; body_plain: string }) {
        const apply = () => {
            // promptToHtml escapes as it paragraph-wraps: a template body with
            // "&" or "<" in it must not reach the editor as raw markup.
            const html = t.body_html || promptToHtml(t.body_plain ?? "");
            onSubjectChange(t.subject || subject);
            onBodyChange(html, t.body_plain || htmlToPlain(html));
            toast.success(`Applied "${t.name}"`);
        };
        if (subject.trim() || htmlToPlain(bodyHtml).trim()) {
            confirm.show(`Replace this content with the "${t.name}" template?`, apply);
        } else {
            apply();
        }
    }

    async function saveAsTemplate() {
        const name = tplName.trim();
        if (!name) return;
        await toast.promise(
            createTemplate.mutateAsync({ name, subject, body_html: bodyHtml, body_plain: htmlToPlain(bodyHtml) }),
            { loading: "Saving template…", success: "Saved to template library.", error: (e: AppError) => buildError(e) },
        );
        setSaveTplOpen(false);
        setTplName("");
    }

    return (
        <div className="space-y-4">
            <div className="flex flex-wrap items-center gap-2">
                <PopoverMenu>
                    <PopoverMenuTrigger asChild>
                        <SelectButton icon={<SparklesIcon className="w-3.5 h-3.5" />} label="Templates" />
                    </PopoverMenuTrigger>
                    <PopoverMenuContent minWidth={260} className="max-h-72 overflow-y-auto p-1">
                        {(templates ?? []).length === 0 ? (
                            <div className="px-2.5 py-3 text-center text-[11.5px] text-slate-400">
                                No templates yet. Save one below.
                            </div>
                        ) : (
                            (templates ?? []).map((t) => (
                                <button
                                    key={t.id}
                                    type="button"
                                    onClick={() => applyTemplate(t)}
                                    className="block w-full rounded px-2.5 py-1.5 text-left hover:bg-slate-100"
                                >
                                    <div className="truncate text-[12.5px] font-medium text-slate-800">{t.name}</div>
                                    <div className="truncate text-[11px] text-slate-400">{t.subject || "No subject"}</div>
                                </button>
                            ))
                        )}
                    </PopoverMenuContent>
                </PopoverMenu>

                <PopoverMenu open={saveTplOpen} onOpenChange={setSaveTplOpen}>
                    <PopoverMenuTrigger asChild>
                        <button
                            type="button"
                            title="Save this content as a reusable template"
                            className="h-7 px-2.5 inline-flex items-center gap-1.5 rounded-md border border-slate-200 bg-white text-[12px] font-medium text-slate-700 transition-colors hover:border-slate-300 hover:text-slate-900"
                        >
                            <BookmarkPlusIcon className="w-3.5 h-3.5" />
                            Save as template
                        </button>
                    </PopoverMenuTrigger>
                    <PopoverMenuContent minWidth={240} className="p-2">
                        <Label>Template name</Label>
                        <div className="flex items-center gap-1.5">
                            <TextInput value={tplName} onChange={setTplName} placeholder="e.g. Cold intro v1" className="flex-1" />
                            <button
                                type="button"
                                onClick={saveAsTemplate}
                                disabled={!tplName.trim() || createTemplate.isPending}
                                className="h-7 px-2.5 rounded-md bg-sky-600 text-[12px] font-medium text-white hover:bg-sky-700 disabled:opacity-50"
                            >
                                {createTemplate.isPending ? <Loader2Icon className="w-3.5 h-3.5 animate-spin" /> : "Save"}
                            </button>
                        </div>
                    </PopoverMenuContent>
                </PopoverMenu>
            </div>

            <div>
                <div className="flex items-center justify-between gap-2 mb-1.5">
                    <Label className="mb-0">Subject</Label>
                    <VariableMenu variables={VARIABLES} onPick={(v) => onSubjectChange(subject + v)} />
                </div>
                <TextInput value={subject} onChange={onSubjectChange} placeholder={subjectPlaceholder} />
            </div>

            <div>
                <div className="flex items-center justify-between gap-2 mb-1.5">
                    <Label className="mb-0">Body</Label>
                    <div className="inline-flex items-center gap-0.5 rounded-md bg-slate-100 p-0.5">
                        <TabBtn active={tab === "edit"} onClick={() => setTab("edit")} icon={<PencilLineIcon className="w-3 h-3" />}>
                            Edit
                        </TabBtn>
                        <TabBtn active={tab === "preview"} onClick={() => setTab("preview")} icon={<EyeIcon className="w-3 h-3" />}>
                            Preview
                        </TabBtn>
                    </div>
                </div>
                {tab === "edit" ? (
                    <RichTextEditor
                        html={bodyHtml}
                        onChange={(html) => onBodyChange(html, htmlToPlain(html))}
                        variables={VARIABLES}
                        links={LINK_VARIABLES}
                        placeholder={bodyPlaceholder}
                    />
                ) : (
                    <div className="rounded-md border border-slate-200 bg-white">
                        {campaignId && (
                            <div className="flex flex-wrap items-center gap-1.5 border-b border-slate-200/70 px-2 py-1.5">
                                <span className="px-1 text-[10px] uppercase tracking-[0.14em] text-slate-400 font-medium">Preview as</span>
                                <PreviewContactPicker campaignId={campaignId} value={previewContact} onChange={setPreviewContact} />
                                <PreviewMailboxPicker inboxes={senders.inboxes} value={previewMailbox} onChange={setPreviewMailbox} />
                                {stepId && canSendTest && (
                                    <div className="ml-auto">
                                        <SendTestButton
                                            campaignId={campaignId}
                                            stepId={stepId}
                                            contact={previewContact}
                                            mailbox={previewMailbox}
                                            dirty={dirty}
                                        />
                                    </div>
                                )}
                            </div>
                        )}
                        {serverPreview?.from && (
                            <div className="border-b border-slate-200/70 px-3 py-2 text-[12.5px]">
                                <span className="text-slate-400">From: </span>
                                <span className="text-slate-800">
                                    {serverPreview.from.name ? `${serverPreview.from.name} <${serverPreview.from.email}>` : serverPreview.from.email}
                                </span>
                            </div>
                        )}
                        <div className="border-b border-slate-200/70 px-3 py-2 text-[12.5px]">
                            <span className="text-slate-400">Subject: </span>
                            <span className="text-slate-800">{(serverPreview?.subject ?? renderPreview(subject)) || "—"}</span>
                        </div>
                        <div
                            className="tiptap-body min-h-[200px] px-3 py-2.5 text-[13px] leading-relaxed text-slate-800"
                            dangerouslySetInnerHTML={{
                                __html:
                                    (serverPreview?.body_html ?? renderPreview(bodyHtml)) ||
                                    (serverPreview?.body_plain
                                        ? `<pre class="whitespace-pre-wrap font-sans">${escapeHtml(serverPreview.body_plain)}</pre>`
                                        : '<p class="text-slate-300">Nothing to preview yet.</p>'),
                            }}
                        />
                        {(serverPreview?.attachments?.length ?? 0) > 0 && (
                            <ul className="flex flex-wrap gap-1.5 border-t border-slate-200/70 px-3 py-2">
                                {serverPreview!.attachments!.map((a) => (
                                    <li
                                        key={a.id}
                                        title={a.mime_type}
                                        className="inline-flex items-center gap-1 rounded-md border border-slate-200 bg-slate-50 px-2 py-0.5 text-[11px] text-slate-700"
                                    >
                                        <PaperclipIcon className="w-3 h-3 text-slate-400" />
                                        <span className="max-w-[200px] truncate">{a.filename}</span>
                                        <span className="text-slate-400">{formatBytes(a.size)}</span>
                                    </li>
                                ))}
                            </ul>
                        )}
                        <p className="border-t border-slate-200/70 px-3 py-1.5 text-[10.5px] text-slate-400">
                            {campaignId
                                ? `Rendered with the real send engine for ${previewContact ? contactLabel(previewContact) : SAMPLE_CONTACT_LABEL}${
                                      previewMailbox ? `, with ${previewMailbox.email}'s signature` : ""
                                  } and the campaign's opt-out footer. Tracking links are not rewritten here.`
                                : `Rendered with the real send engine for ${SAMPLE_CONTACT_LABEL}. Conditionals, functions and spintax all run.`}
                        </p>
                    </div>
                )}
                {(serverPreview?.errors?.length ?? 0) > 0 ? (
                    <p className="mt-1.5 flex items-start gap-1.5 text-[11px] text-rose-600">
                        <AlertCircleIcon className="mt-px w-3.5 h-3.5 shrink-0" />
                        <span>{serverPreview!.errors!.join(" · ")} — fix before sending.</span>
                    </p>
                ) : tplIssue ? (
                    <p className="mt-1.5 flex items-center gap-1.5 text-[11px] text-amber-600">
                        <AlertCircleIcon className="w-3.5 h-3.5 shrink-0" />
                        {tplIssue} It&apos;ll fall back to plain text — fix it before sending.
                    </p>
                ) : null}
                {(serverPreview?.unresolved?.length ?? 0) > 0 && (
                    <p className="mt-1.5 flex items-start gap-1.5 text-[11px] text-amber-600">
                        <AlertCircleIcon className="mt-px w-3.5 h-3.5 shrink-0" />
                        <span>
                            These won&apos;t resolve and would send literally:{" "}
                            <span className="font-mono">{serverPreview!.unresolved!.join(", ")}</span>
                        </span>
                    </p>
                )}
            </div>

            <ContentScore subject={subject} bodyHtml={bodyHtml} bodyPlain={htmlToPlain(bodyHtml)} />
        </div>
    );
}

function escapeHtml(s: string): string {
    return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

function TabBtn({
    active,
    onClick,
    icon,
    children,
}: {
    active: boolean;
    onClick: () => void;
    icon: React.ReactNode;
    children: React.ReactNode;
}) {
    return (
        <button
            type="button"
            onClick={onClick}
            className={`h-6 px-2 inline-flex items-center gap-1 rounded text-[11.5px] font-medium transition-colors ${
                active ? "bg-white text-slate-900 shadow-sm" : "text-slate-500 hover:text-slate-900"
            }`}
        >
            {icon}
            {children}
        </button>
    );
}
