// QslDesignerModal — the QSL card designer. Flow: drop/choose photos →
// "Generate designs" (3 automatic proposals from the placement engine) →
// pick one → live editor (click/drag elements, style controls) → save.
// Saved templates are listed with thumbnails and can be edited, deleted or
// marked default (used by "Send eQSL by e-mail" on the QSO grid).

import { useEffect, useRef, useState } from 'react';
import { Images, Loader2, Sparkles, Star, Trash2, Pencil, ChevronLeft } from 'lucide-react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import {
  QSLPickPhotos, QSLGenerateProposals, QSLListTemplates, QSLGetTemplate,
  QSLListOtherProfileTemplates, QSLCopyTemplateToActiveProfile,
  QSLSaveTemplate, QSLSetDefaultTemplate, QSLDeleteTemplate, QSLSavePreview,
  QSLPreviewDataURL, QSLResolvePreview, QSLStylePresets,
} from '../../../wailsjs/go/main/App';
import type {
  CardTemplate, CardElement, QSOBox, RenderModel, QSLTemplateInfo, QSLPresetInfo,
} from './qslTypes';
import { loadCardAssets, loadFonts, type CardAssets } from './qslAssets';
import { CardPreview, type CardSelection } from './CardPreview';
import { EditorPanel } from './EditorPanel';
import { rasterizeCard } from './rasterize';

interface Props {
  open: boolean;
  onClose: () => void;
}

interface Proposal {
  template: CardTemplate;
  model: RenderModel;
  assets: CardAssets;
}

interface Editing {
  templateId: number; // 0 = new (photos still at their original paths)
  template: CardTemplate; // raw document ({placeholders})
  model: RenderModel; // resolved copy for display
  assets: CardAssets;
}

type View = 'home' | 'proposals' | 'editor';

async function resolveModel(template: CardTemplate): Promise<RenderModel> {
  return JSON.parse(await QSLResolvePreview(JSON.stringify(template))) as RenderModel;
}

export function QslDesignerModal({ open, onClose }: Props) {
  const [view, setView] = useState<View>('home');
  const [photoPaths, setPhotoPaths] = useState<string[]>([]);
  const [proposals, setProposals] = useState<Proposal[]>([]);
  const [editing, setEditing] = useState<Editing | null>(null);
  const [sel, setSel] = useState<CardSelection>(null);
  const [name, setName] = useState('');
  const [asDefault, setAsDefault] = useState(false);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');
  const [saved, setSaved] = useState<QSLTemplateInfo[]>([]);
  const [foreign, setForeign] = useState<any[]>([]);
  const [previews, setPreviews] = useState<Record<number, string>>({});
  const [presets, setPresets] = useState<QSLPresetInfo[]>([]);
  const [fontFamilies, setFontFamilies] = useState<string[]>([]);
  const [deleteArm, setDeleteArm] = useState(0);
  const svgEl = useRef<SVGSVGElement | null>(null);

  useEffect(() => {
    if (!open) return;
    setView('home');
    setError('');
    setPhotoPaths([]);
    setProposals([]);
    setEditing(null);
    void refreshSaved();
    void QSLStylePresets().then((p) => setPresets(p as QSLPresetInfo[]));
    void loadFonts().then(({ fonts }) =>
      setFontFamilies([...fonts.map((f) => f.family), 'system-bold-sans']));
  }, [open]);

  async function refreshSaved() {
    try {
      const list = ((await QSLListTemplates()) ?? []) as QSLTemplateInfo[];
      setSaved(list);
      const p: Record<number, string> = {};
      await Promise.all(list.map(async (t) => {
        const url = await QSLPreviewDataURL(t.id);
        if (url) p[t.id] = url;
      }));
      setPreviews(p);
      // The designs this profile cannot see, offered for copying.
      try { setForeign(((await QSLListOtherProfileTemplates()) ?? []) as any[]); } catch { /* nothing to offer */ }
    } catch (e) {
      setError(String(e));
    }
  }

  async function choosePhotos() {
    try {
      const paths = ((await QSLPickPhotos()) ?? []) as string[];
      if (paths.length) setPhotoPaths(paths.slice(0, 5));
    } catch (e) {
      setError(String(e));
    }
  }

  async function generate() {
    setBusy(true);
    setError('');
    try {
      const docs = (await QSLGenerateProposals(photoPaths)) as string[];
      const out: Proposal[] = [];
      for (const doc of docs) {
        const template = JSON.parse(doc) as CardTemplate;
        const model = await resolveModel(template);
        const assets = await loadCardAssets(model.template, 0);
        out.push({ template, model, assets });
      }
      setProposals(out);
      setView('proposals');
    } catch (e) {
      setError(String(e));
    } finally {
      setBusy(false);
    }
  }

  function openEditor(templateId: number, template: CardTemplate, model: RenderModel, assets: CardAssets) {
    setEditing({ templateId, template, model, assets });
    setName(template.name);
    setAsDefault(false);
    setSel(null);
    setError('');
    setView('editor');
  }

  async function editSaved(info: QSLTemplateInfo) {
    setBusy(true);
    setError('');
    try {
      const template = JSON.parse(await QSLGetTemplate(info.id)) as CardTemplate;
      const model = await resolveModel(template);
      const assets = await loadCardAssets(model.template, info.id);
      openEditor(info.id, template, model, assets);
      setAsDefault(info.is_default);
    } catch (e) {
      setError(String(e));
    } finally {
      setBusy(false);
    }
  }

  // Edits patch the raw template AND the resolved display copy in lock-step;
  // only a text change needs a backend re-resolve (placeholders may differ).
  function patchBoth(fn: (t: CardTemplate) => void, reResolve = false) {
    setEditing((cur) => {
      if (!cur) return cur;
      const template = structuredClone(cur.template);
      const model = structuredClone(cur.model);
      fn(template);
      fn(model.template);
      if (reResolve) {
        void resolveModel(template).then((m) =>
          setEditing((c) => (c ? { ...c, model: m } : c)));
      }
      return { ...cur, template, model };
    });
  }

  const patchElement = (idx: number, patch: Partial<CardElement>) =>
    patchBoth((t) => Object.assign(t.elements[idx], patch), 'text' in patch);

  const patchBox = (patch: Partial<QSOBox>) =>
    patchBoth((t) => { if (t.qso_box) Object.assign(t.qso_box, patch); }, 'footer' in patch || 'title' in patch);

  const onMove = (target: Exclude<CardSelection, null>, x: number, y: number) =>
    patchBoth((t) => {
      if (target === 'box') {
        if (t.qso_box) { t.qso_box.x = x; t.qso_box.y = y; }
      } else {
        t.elements[target].x = x;
        t.elements[target].y = y;
      }
    });

  const onScrim = (enabled: boolean) =>
    patchBoth((t) => {
      t.hero.scrim = { enabled, color: t.hero.scrim?.color ?? '#0b1a2e', opacity: t.hero.scrim?.opacity ?? 0.25 };
    });

  async function save() {
    if (!editing) return;
    setBusy(true);
    setError('');
    try {
      const id = await QSLSaveTemplate(editing.templateId, name, JSON.stringify(editing.template), true);
      if (svgEl.current) {
        const card = editing.template.card;
        const png = await rasterizeCard(svgEl.current, 480, Math.round(480 * (card.h / card.w)), 'image/png');
        await QSLSavePreview(id, png);
      }
      if (asDefault) await QSLSetDefaultTemplate(id);
      await refreshSaved();
      setView('home');
      setEditing(null);
    } catch (e) {
      setError(String(e));
    } finally {
      setBusy(false);
    }
  }

  async function removeTemplate(id: number) {
    if (deleteArm !== id) { setDeleteArm(id); return; }
    setDeleteArm(0);
    try {
      await QSLDeleteTemplate(id);
      await refreshSaved();
    } catch (e) {
      setError(String(e));
    }
  }

  async function makeDefault(id: number) {
    try {
      await QSLSetDefaultTemplate(id);
      await refreshSaved();
    } catch (e) {
      setError(String(e));
    }
  }

  return (
    <Dialog open={open} onOpenChange={(v) => !v && onClose()}>
      <DialogContent className="max-w-[1260px]">
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2">
            <Sparkles className="size-5 text-warning" />
            QSL card designer
            {view !== 'home' && (
              <Button variant="ghost" size="sm" className="ml-2 h-7 px-2 text-xs"
                onClick={() => setView(view === 'editor' && proposals.length && editing?.templateId === 0 ? 'proposals' : 'home')}>
                <ChevronLeft className="size-3.5" /> Back
              </Button>
            )}
          </DialogTitle>
        </DialogHeader>

        <div className="max-h-[78vh] space-y-4 overflow-y-auto px-6 py-5">
        {error && (
          <div className="rounded border border-destructive bg-destructive-muted px-3 py-1.5 text-sm text-destructive-muted-foreground">{error}</div>
        )}

        {view === 'home' && (
          <div className="space-y-5">
            <section className="space-y-2">
              <h3 className="text-sm font-semibold">New design</h3>
              <p className="text-xs text-muted-foreground">
                Pick 1–5 photos — OpsLog analyzes them and proposes three card designs
                with your callsign, name, zones and country placed automatically.
              </p>
              <div className="flex items-center gap-2">
                <Button variant="outline" size="sm" onClick={choosePhotos}>
                  <Images className="mr-1 size-4" /> Choose photos…
                </Button>
                <Button size="sm" disabled={!photoPaths.length || busy} onClick={generate}>
                  {busy ? <Loader2 className="mr-1 size-4 animate-spin" /> : <Sparkles className="mr-1 size-4" />}
                  Generate designs
                </Button>
                {photoPaths.length > 0 && (
                  <span className="text-xs text-muted-foreground">
                    {photoPaths.length} photo{photoPaths.length > 1 ? 's' : ''} selected
                  </span>
                )}
              </div>
            </section>

            <section className="space-y-2">
              <div className="flex items-center gap-3">
                <h3 className="text-sm font-semibold">Saved templates</h3>
                {/* Designs owned by another profile. A second profile is usually
                    the same operator with a different rig or locator — same
                    callsign, same cards — and redrawing one because the antenna
                    changed is work nobody should have to do. */}
                {!!foreign.length && (
                  <select
                    value=""
                    onChange={async (e) => {
                      const id = parseInt(e.target.value, 10);
                      if (!id) return;
                      try { await QSLCopyTemplateToActiveProfile(id); await refreshSaved(); }
                      catch (err) { setError(String(err)); }
                    }}
                    className="h-7 rounded-md border border-border bg-background px-2 text-xs">
                    <option value="">Copy from another profile…</option>
                    {foreign.map((f) => (
                      <option key={f.id} value={f.id}>{f.profile_name} — {f.name}</option>
                    ))}
                  </select>
                )}
              </div>
              {!saved.length && <p className="text-xs text-muted-foreground">None yet.</p>}
              <div className="grid grid-cols-3 gap-3">
                {saved.map((t) => (
                  <div key={t.id} className="rounded-md border border-border p-2">
                    {previews[t.id]
                      ? <img src={previews[t.id]} alt={t.name} className="w-full rounded" />
                      : <div className="flex h-28 items-center justify-center rounded bg-muted text-xs text-muted-foreground">no preview</div>}
                    <div className="mt-1.5 flex items-center justify-between gap-1">
                      <span className="truncate text-sm font-medium">
                        {t.is_default && <Star className="mr-1 inline size-3.5 fill-warning text-warning" />}
                        {t.name}
                      </span>
                      <span className="flex shrink-0 gap-0.5">
                        <Button variant="ghost" size="sm" className="h-7 w-7 p-0" title="Edit"
                          onClick={() => editSaved(t)}>
                          <Pencil className="size-3.5" />
                        </Button>
                        {!t.is_default && (
                          <Button variant="ghost" size="sm" className="h-7 w-7 p-0" title="Set as default"
                            onClick={() => makeDefault(t.id)}>
                            <Star className="size-3.5" />
                          </Button>
                        )}
                        <Button variant="ghost" size="sm"
                          className={`h-7 p-0 ${deleteArm === t.id ? 'w-auto px-1.5 text-destructive' : 'w-7'}`}
                          title="Delete" onClick={() => removeTemplate(t.id)}>
                          {deleteArm === t.id ? <span className="text-xs">Sure?</span> : <Trash2 className="size-3.5" />}
                        </Button>
                      </span>
                    </div>
                  </div>
                ))}
              </div>
            </section>

            <p className="text-xs text-muted-foreground">
              The eQSL e-mail message and the auto-send option are in Settings → E-mail (SMTP).
            </p>
          </div>
        )}

        {view === 'proposals' && (
          <div className="space-y-3">
            <p className="text-sm text-muted-foreground">Pick a design to refine it:</p>
            <div className="grid grid-cols-3 gap-3">
              {proposals.map((p, i) => (
                <button
                  key={i}
                  className="rounded-md border-2 border-transparent p-1 transition hover:border-info"
                  onClick={() => openEditor(0, p.template, p.model, p.assets)}
                >
                  <CardPreview model={p.model} assets={p.assets} width={352} />
                </button>
              ))}
            </div>
          </div>
        )}

        {view === 'editor' && editing && (
          <div className="flex gap-4">
            <div className="min-w-0 flex-1 space-y-3">
              <div className="rounded-md bg-slate-800/60 p-3">
                <CardPreview
                  model={editing.model}
                  assets={editing.assets}
                  width={760}
                  selected={sel}
                  onSelect={setSel}
                  onMove={onMove}
                  svgRef={(el) => { svgEl.current = el; }}
                />
              </div>
              <div className="flex items-center gap-2">
                <Label className="text-xs text-muted-foreground">Name</Label>
                <Input className="h-8 w-56" value={name} onChange={(e) => setName(e.target.value)}
                  placeholder="e.g. Vietnam sunset" />
                <label className="flex items-center gap-1.5 text-sm">
                  <Checkbox checked={asDefault} onCheckedChange={(v) => setAsDefault(v === true)} />
                  Default for this profile
                </label>
                <div className="flex-1" />
                <Button size="sm" disabled={busy || !name.trim()} onClick={save}>
                  {busy && <Loader2 className="mr-1 size-4 animate-spin" />} Save template
                </Button>
              </div>
            </div>
            <EditorPanel
              template={editing.template}
              sel={sel}
              presets={presets}
              fontFamilies={fontFamilies}
              onPatchElement={patchElement}
              onPatchBox={patchBox}
              onScrim={onScrim}
              onSelect={setSel}
            />
          </div>
        )}
        </div>
      </DialogContent>
    </Dialog>
  );
}
