import { useEffect, useMemo, useRef, useState } from "react";
import { ArrowBigLeft, ArrowBigLeftDash, ArrowBigRight, ArrowBigRightDash } from "lucide-react";
import type { ApiSettings } from "../../types";
import Button from "../../components/Button";
import Card from "../../components/Card";
import FormField from "../../components/FormField";
import PageTitle from "../../components/PageTitle";
import LoadingFrame from "../../components/LoadingFrame";
import LockedVersionNotice from "./components/LockedVersionNotice";
import VersionLine from "./components/VersionLine";
import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
import { useCampaignDraftEditor } from "./hooks/useCampaignDraftEditor";
import { asArray, asRecord, isAuditLockedVersion } from "./utils/campaignView";
import { cloneJson, getBool, getText } from "./utils/draftEditor";
import { buildEffectiveAttachmentPreviews, type EffectiveAttachmentPreview } from "./utils/attachments";
import { humanizeFieldName } from "./utils/fieldDefinitions";
import { buildTemplatePreviewContext, buildUndefinedPlaceholders, extractTemplatePlaceholders, removePlaceholderFromText, renderTemplatePreviewText, uniquePlaceholders, valueToPreview, type TemplateNamespace, type TemplatePlaceholder, type UndefinedPlaceholder } from "./utils/templatePlaceholders";

type BodyMode = "text" | "html";
type EditorTarget = "subject" | "text" | "html";

export default function TemplateDataPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
  const { data, loading, error, reload, setError } = useCampaignWorkspaceData(settings, campaignId);
  const [bodyMode, setBodyMode] = useState<BodyMode>("text");
  const [activeEditor, setActiveEditor] = useState<EditorTarget>("text");
  const [previewOpen, setPreviewOpen] = useState(false);
  const [previewIndex, setPreviewIndex] = useState(0);
  const [undefinedDialog, setUndefinedDialog] = useState<UndefinedPlaceholder | null>(null);
  const subjectRef = useRef<HTMLInputElement | null>(null);
  const textRef = useRef<HTMLTextAreaElement | null>(null);
  const htmlRef = useRef<HTMLTextAreaElement | null>(null);

  const version = data.currentVersion;
  const locked = isAuditLockedVersion(version);
  const { draft, setDraft, displayDraft, dirty, saveState, localError, patch, markDirty, saveDraft } = useCampaignDraftEditor({
    settings,
    campaignId,
    version,
    locked,
    reload,
    setError,
    currentStep: "template",
    unsavedTitle: "Unsaved template changes",
    unsavedMessage: "The template has unsaved changes. Save them before leaving, or discard them and continue.",
    loadedLabel: (loadedVersion) => loadedVersion.autosaved_at ? `Loaded autosave ${new Date(loadedVersion.autosaved_at).toLocaleString()}` : "Loaded",
    onLoaded: () => setPreviewIndex(0)
  });
  const template = asRecord(displayDraft.template);
  const fields = useMemo(() => asArray(displayDraft.fields).map(asRecord), [displayDraft.fields]);
  const localFieldNames = useMemo(() => fields.map((field) => String(field.name || field.id || "")).filter(Boolean), [fields]);
  const globalFieldNames = useMemo(() => uniqueSorted([...localFieldNames, ...Object.keys(asRecord(displayDraft.global_values))]), [displayDraft.global_values, localFieldNames]);
  const allAvailableNames = useMemo(() => new Set([...localFieldNames, ...globalFieldNames]), [localFieldNames, globalFieldNames]);
  const entries = asRecord(displayDraft.entries);
  const inlineEntries = useMemo(() => asArray(entries.inline).map(asRecord), [entries.inline]);
  const previewEntries = inlineEntries.length > 0 ? inlineEntries : [{}];
  const previewEntry = previewEntries[Math.min(previewIndex, previewEntries.length - 1)] ?? {};
  const ignoreEmptyFields = getBool(asRecord(displayDraft.validation_policy), "ignore_empty_fields", false);
  const templateText = `${getText(template, "subject")}\n${getText(template, "text")}\n${getText(template, "html")}`;
  const usedPlaceholders = useMemo(() => extractTemplatePlaceholders(templateText), [templateText]);
  const invalidNamespacePlaceholders = useMemo(() => uniquePlaceholders(usedPlaceholders.filter((field) => !field.validNamespace)), [usedPlaceholders]);
  const undefinedPlaceholders = useMemo(() => buildUndefinedPlaceholders(usedPlaceholders, allAvailableNames), [usedPlaceholders, allAvailableNames]);
  const previewContext = useMemo(() => buildTemplatePreviewContext(displayDraft, previewEntry), [displayDraft, previewEntry]);
  const previewSubject = renderTemplatePreviewText(getText(template, "subject"), previewContext, ignoreEmptyFields);
  const previewText = renderTemplatePreviewText(getText(template, "text"), previewContext, ignoreEmptyFields);
  const previewHtml = renderTemplatePreviewText(getText(template, "html"), previewContext, ignoreEmptyFields);
  const previewAttachments = useMemo(() => buildEffectiveAttachmentPreviews(displayDraft, previewEntry), [displayDraft, previewEntry]);


  useEffect(() => {
    if (previewIndex >= previewEntries.length) setPreviewIndex(Math.max(0, previewEntries.length - 1));
  }, [previewIndex, previewEntries.length]);


  function patchTemplateText(target: EditorTarget, value: string) {
    patch(["template", target], value);
  }

  function insertPlaceholder(namespace: TemplateNamespace, name: string) {
    if (locked) return;
    const target = bodyMode === "html" && activeEditor !== "subject" ? "html" : activeEditor;
    const element = target === "subject" ? subjectRef.current : target === "html" ? htmlRef.current : textRef.current;
    const token = `{{${namespace}:${name}}}`;
    const currentText = getText(template, target);
    const start = element?.selectionStart ?? currentText.length;
    const end = element?.selectionEnd ?? currentText.length;
    const nextText = `${currentText.slice(0, start)}${token}${currentText.slice(end)}`;
    patchTemplateText(target, nextText);
    window.requestAnimationFrame(() => {
      element?.focus();
      const cursor = start + token.length;
      element?.setSelectionRange(cursor, cursor);
    });
  }

  function addUndefinedField(field: UndefinedPlaceholder) {
    if (!draft || locked || !field.name) return;
    const existingFields = asArray(draft.fields).map(asRecord);
    const alreadyDefined = existingFields.some((item) => String(item.name || item.id || "") === field.name);
    if (!alreadyDefined) {
      patch(["fields"], [
        ...existingFields,
        {
          name: field.name,
          label: humanizeFieldName(field.name),
          type: "string",
          required: false,
          can_override: true
        }
      ]);
    }
    setUndefinedDialog(null);
  }

  function removePlaceholder(field: UndefinedPlaceholder) {
    if (locked) return;
    setDraft((current) => {
      const next = cloneJson(current ?? {});
      const nextTemplate = { ...asRecord(next.template) };
      nextTemplate.subject = removePlaceholderFromText(getText(nextTemplate, "subject"), field.raw);
      nextTemplate.text = removePlaceholderFromText(getText(nextTemplate, "text"), field.raw);
      nextTemplate.html = removePlaceholderFromText(getText(nextTemplate, "html"), field.raw);
      next.template = nextTemplate;
      return next;
    });
    markDirty();
    setUndefinedDialog(null);
  }


  return (
    <div className="content-pad workspace-data-page">
      <div className="page-heading split workspace-heading">
        <div>
          <PageTitle loading={loading}>Template</PageTitle>
          <VersionLine version={version} versions={data.versions} status={saveState} />
        </div>
        <div className="button-row compact-actions">
          <Button disabled>Manage templates</Button>
          <Button onClick={reload} disabled={loading}>Reload</Button>
          <Button variant="primary" onClick={() => saveDraft("manual")} disabled={!dirty || locked || !draft}>Save</Button>
        </div>
      </div>

      {error && <div className="alert danger">{error}</div>}
      {localError && <div className="alert danger">{localError}</div>}
      {locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} reload={reload} message="Create an editable copy before changing the template." />}

      <LoadingFrame loading={loading || !draft} label="Loading campaign draft…">
        <>
          <div className="dashboard-grid template-editor-grid">
            <Card title="Editable template" actions={<Button onClick={() => setPreviewOpen(true)}>Preview</Button>}>
              <div className="form-grid">
                <FormField label="Subject">
                  <input
                    ref={subjectRef}
                    value={getText(template, "subject")}
                    disabled={locked}
                    onFocus={() => setActiveEditor("subject")}
                    onChange={(event) => patchTemplateText("subject", event.target.value)}
                  />
                </FormField>
                <div className="template-body-mode" role="tablist" aria-label="Template body mode">
                  <button type="button" className={bodyMode === "text" ? "active" : ""} onClick={() => { setBodyMode("text"); setActiveEditor("text"); }}>Plain text</button>
                  <button type="button" className={bodyMode === "html" ? "active" : ""} onClick={() => { setBodyMode("html"); setActiveEditor("html"); }}>HTML</button>
                </div>
                {bodyMode === "text" && (
                  <FormField label="Plain text body">
                    <textarea
                      ref={textRef}
                      rows={16}
                      value={getText(template, "text")}
                      disabled={locked}
                      onFocus={() => setActiveEditor("text")}
                      onChange={(event) => patchTemplateText("text", event.target.value)}
                    />
                  </FormField>
                )}
                {bodyMode === "html" && (
                  <FormField label="HTML body">
                    <textarea
                      ref={htmlRef}
                      rows={16}
                      value={getText(template, "html")}
                      disabled={locked}
                      onFocus={() => setActiveEditor("html")}
                      onChange={(event) => patchTemplateText("html", event.target.value)}
                    />
                  </FormField>
                )}
                <div className="button-row template-editor-actions">
                  <Button disabled>Load from library</Button>
                  <Button disabled>Save to library</Button>
                </div>
              </div>
            </Card>

            <div className="template-side-stack">
              <Card title="Fields">
                {invalidNamespacePlaceholders.length > 0 && (
                  <div className="alert warning">Undefined placeholder namespace detected: {invalidNamespacePlaceholders.map((field) => field.namespace || field.raw).join(", ")}.</div>
                )}
                {usedPlaceholders.length === 0 && <p className="muted">No template placeholders detected yet.</p>}
                <p className="muted small-note">Click a field to insert it at the current cursor position as a namespaced placeholder.</p>

                <h3 className="section-mini-heading">Global fields</h3>
                <TemplateFieldChipList
                  namespace="global"
                  names={globalFieldNames}
                  usedPlaceholders={usedPlaceholders}
                  empty="No campaign fields or global values defined."
                  onInsert={insertPlaceholder}
                />

                <h3 className="section-mini-heading">Local fields</h3>
                <TemplateFieldChipList
                  namespace="local"
                  names={localFieldNames}
                  usedPlaceholders={usedPlaceholders}
                  empty="No campaign fields defined."
                  onInsert={insertPlaceholder}
                />

                <h3 className="section-mini-heading">Used in template, but undefined</h3>
                <UndefinedPlaceholderList items={undefinedPlaceholders} onSelect={setUndefinedDialog} />
              </Card>
            </div>
          </div>

          <div className="button-row page-bottom-actions">
            <Button variant="primary" onClick={() => saveDraft("manual")} disabled={!dirty || locked || !draft}>Save</Button>
          </div>
        </>
      </LoadingFrame>

      {previewOpen && (
        <TemplatePreviewOverlay
          bodyMode={bodyMode}
          entry={previewEntry}
          index={Math.min(previewIndex, previewEntries.length - 1)}
          total={previewEntries.length}
          subject={previewSubject}
          text={previewText}
          html={previewHtml}
          hasRealRecipients={inlineEntries.length > 0}
          attachments={previewAttachments}
          onClose={() => setPreviewOpen(false)}
          onFirst={() => setPreviewIndex(0)}
          onPrevious={() => setPreviewIndex((value) => Math.max(0, value - 1))}
          onNext={() => setPreviewIndex((value) => Math.min(previewEntries.length - 1, value + 1))}
          onLast={() => setPreviewIndex(previewEntries.length - 1)}
        />
      )}

      {undefinedDialog && (
        <div className="overlay-backdrop" role="dialog" aria-modal="true" aria-labelledby="undefined-template-field-title">
          <div className="modal-panel template-action-dialog">
            <header className="modal-header">
              <h2 id="undefined-template-field-title">Undefined template field</h2>
              <button className="modal-close" onClick={() => setUndefinedDialog(null)}>×</button>
            </header>
            <div className="modal-body">
              <p>The template uses <code>{`{{${undefinedDialog.raw}}}`}</code>, but it cannot be matched to a known field.</p>
              {undefinedDialog.reason === "invalid-namespace" && <div className="alert warning">Use the namespace <code>global:</code> or <code>local:</code>.</div>}
              {undefinedDialog.reason === "missing-field" && <p className="muted">You can add the name <strong>{undefinedDialog.name}</strong> as a campaign field, or remove this placeholder from subject, plain text and HTML.</p>}
            </div>
            <footer className="modal-footer">
              <Button onClick={() => setUndefinedDialog(null)}>Cancel</Button>
              <Button onClick={() => removePlaceholder(undefinedDialog)}>Remove from template</Button>
              <Button variant="primary" onClick={() => addUndefinedField(undefinedDialog)} disabled={!undefinedDialog.name}>Add field</Button>
            </footer>
          </div>
        </div>
      )}
    </div>
  );
}

function TemplateFieldChipList({
  namespace,
  names,
  usedPlaceholders,
  empty,
  onInsert
}: {
  namespace: TemplateNamespace;
  names: string[];
  usedPlaceholders: TemplatePlaceholder[];
  empty: string;
  onInsert: (namespace: TemplateNamespace, name: string) => void;
}) {
  if (names.length === 0) return <p className="muted">{empty}</p>;
  return (
    <div className="field-chip-list">
      {names.map((name) => {
        const used = usedPlaceholders.some((field) => field.validNamespace && field.namespace === namespace && field.name === name);
        return (
          <button type="button" className={`field-chip field-chip-button ${used ? "used" : ""}`} key={`${namespace}:${name}`} onClick={() => onInsert(namespace, name)}>
            <span className="field-chip-namespace">{namespace}</span>{name}
          </button>
        );
      })}
    </div>
  );
}

function UndefinedPlaceholderList({ items, onSelect }: { items: UndefinedPlaceholder[]; onSelect: (item: UndefinedPlaceholder) => void }) {
  if (items.length === 0) return <p className="muted">No undefined placeholders detected.</p>;
  return (
    <div className="field-chip-list">
      {items.map((item) => (
        <button type="button" className="field-chip field-chip-button undefined" key={`${item.raw}:${item.reason}`} onClick={() => onSelect(item)}>
          {item.display}
        </button>
      ))}
    </div>
  );
}

function TemplatePreviewOverlay({
  bodyMode,
  entry,
  index,
  total,
  subject,
  text,
  html,
  hasRealRecipients,
  attachments,
  onClose,
  onFirst,
  onPrevious,
  onNext,
  onLast
}: {
  bodyMode: BodyMode;
  entry: Record<string, unknown>;
  index: number;
  total: number;
  subject: string;
  text: string;
  html: string;
  hasRealRecipients: boolean;
  attachments: EffectiveAttachmentPreview[];
  onClose: () => void;
  onFirst: () => void;
  onPrevious: () => void;
  onNext: () => void;
  onLast: () => void;
}) {
  return (
    <div className="overlay-backdrop" role="dialog" aria-modal="true" aria-labelledby="template-preview-title">
      <div className="modal-panel template-preview-modal">
        <header className="modal-header">
          <h2 id="template-preview-title">Template preview</h2>
          <button className="modal-close" onClick={onClose}>×</button>
        </header>
        <div className="modal-body">
          <div className="template-preview-toolbar">
            <div>
              <strong>{hasRealRecipients ? recipientLabel(entry, index) : "Global preview"}</strong>
              <p className="muted small-note">{hasRealRecipients ? `${index + 1} of ${total}` : "No inline recipients are available yet."}</p>
            </div>
            <div className="button-row compact-actions template-preview-nav" aria-label="Preview message navigation">
              <button type="button" className="version-arrow" onClick={onFirst} disabled={index <= 0} title="First message" aria-label="First message"><ArrowBigLeftDash aria-hidden="true" /></button>
              <button type="button" className="version-arrow" onClick={onPrevious} disabled={index <= 0} title="Previous message" aria-label="Previous message"><ArrowBigLeft aria-hidden="true" /></button>
              <span className="template-preview-count">{index + 1} / {total}</span>
              <button type="button" className="version-arrow" onClick={onNext} disabled={index >= total - 1} title="Next message" aria-label="Next message"><ArrowBigRight aria-hidden="true" /></button>
              <button type="button" className="version-arrow" onClick={onLast} disabled={index >= total - 1} title="Last message" aria-label="Last message"><ArrowBigRightDash aria-hidden="true" /></button>
            </div>
          </div>
          <div className="template-preview-box">
            <h3>{subject || "No subject"}</h3>
            {bodyMode === "html" ? (
              <iframe className="template-preview-frame" title="Rendered HTML body preview" sandbox="" srcDoc={html || "<p>No HTML body to preview.</p>"} />
            ) : (
              <pre>{text || "No plain-text body to preview."}</pre>
            )}
          </div>
          <EffectiveAttachmentPreviewTable attachments={attachments} />
        </div>
        <footer className="modal-footer"><Button variant="primary" onClick={onClose}>Close</Button></footer>
      </div>
    </div>
  );
}


function EffectiveAttachmentPreviewTable({ attachments }: { attachments: EffectiveAttachmentPreview[] }) {
  return (
    <div className="template-preview-attachments">
      <h3>Effective attachments</h3>
      {attachments.length === 0 ? (
        <p className="muted small-note">No global or recipient attachments are effective for this message.</p>
      ) : (
        <div className="app-table-wrap">
          <table className="app-table compact-table">
            <thead>
              <tr>
                <th>Scope</th>
                <th>Label</th>
                <th>Base path</th>
                <th>File / pattern</th>
                <th>Options</th>
                <th>Preview match</th>
              </tr>
            </thead>
            <tbody>
              {attachments.map((attachment, index) => (
                <tr key={`${attachment.scope}:${attachment.basePath}:${attachment.fileFilter}:${index}`}>
                  <td>{attachment.scope === "global" ? "Global" : "Recipient"}</td>
                  <td>{attachment.label}</td>
                  <td>{attachment.basePathName}<br /><span className="muted"><code>{attachment.basePath}</code></span></td>
                  <td><code>{attachment.fileFilter || "—"}</code></td>
                  <td>{attachment.required ? "required" : "optional"}{attachment.includeSubdirs ? ", subdirs" : ""}</td>
                  <td>{attachment.matches.length > 0 ? attachment.matches.join(", ") : "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}

function recipientLabel(entry: Record<string, unknown>, index: number): string {
  const name = valueToPreview(entry.name).trim();
  const email = valueToPreview(entry.email).trim();
  if (name && email) return `${name} <${email}>`;
  if (name) return name;
  if (email) return email;
  return `Recipient ${index + 1}`;
}

function uniqueSorted(values: string[]): string[] {
  return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort();
}
