import { useEffect, useState, useMemo, useCallback } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
  History as HistoryIcon,
  X,
  Trash2,
  Search,
  RefreshCw,
  ScrollText,
  Sparkles,
  Eye,
  Loader2,
} from "lucide-react";
import { useToast } from "./Toast";
import {
  listGenerations,
  getGeneration,
  deleteGeneration,
  clearAllGenerations,
  relativeTime,
  extractTitle,
  KIND_LABELS,
  KIND_ORDER,
  type Generation,
  type GenerationKind,
  type GenerationSummary,
} from "../lib/generations";
import { ensureArray, flattenValue, parseLlmJson } from "../lib/llm-parse";

interface HistoryViewProps {
  // ponytail: optional callback when a row is rehydrated into a tool.
  // The App shell wires this to set the active view and prefill the form.
  onRehydrate?: (kind: GenerationKind, data: Generation) => void;
}

export function HistoryView({ onRehydrate }: HistoryViewProps) {
  const [summaries, setSummaries] = useState<GenerationSummary[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [query, setQuery] = useState("");
  const [activeKind, setActiveKind] = useState<GenerationKind | "all">("all");
  const [selected, setSelected] = useState<Generation | null>(null);
  const [selectedLoading, setSelectedLoading] = useState(false);
  const [confirmClear, setConfirmClear] = useState(false);
  const { addToast } = useToast();

  const refresh = useCallback(async () => {
    setLoading(true);
    setError("");
    try {
      const list = await listGenerations();
      setSummaries(list);
    } catch (e) {
      setError(String(e));
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    refresh();
  }, [refresh]);

  const grouped = useMemo(() => {
    const byKind = new Map<GenerationKind, GenerationSummary[]>();
    for (const k of KIND_ORDER) byKind.set(k, []);
    for (const s of summaries) {
      const list = byKind.get(s.kind);
      if (list) list.push(s);
    }
    return byKind;
  }, [summaries]);

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    const rows: GenerationSummary[] = [];
    for (const [, list] of grouped) {
      for (const s of list) {
        if (activeKind !== "all" && s.kind !== activeKind) continue;
        if (q && !s.title.toLowerCase().includes(q)) continue;
        rows.push(s);
      }
    }
    return rows;
  }, [grouped, query, activeKind]);

  const onSelect = useCallback(async (id: number) => {
    setSelectedLoading(true);
    try {
      const g = await getGeneration(id);
      setSelected(g);
    } catch (e) {
      addToast(`Failed to load: ${e}`, "error");
    } finally {
      setSelectedLoading(false);
    }
  }, [addToast]);

  const onDelete = useCallback(
    async (id: number) => {
      try {
        await deleteGeneration(id);
        setSummaries((prev) => prev.filter((s) => s.id !== id));
        if (selected?.id === id) setSelected(null);
      } catch (e) {
        addToast(`Delete failed: ${e}`, "error");
      }
    },
    [selected, addToast],
  );

  const onClearAll = useCallback(async () => {
    try {
      const n = await clearAllGenerations();
      addToast(`Cleared ${n} generation${n === 1 ? "" : "s"}`, "success");
      setSummaries([]);
      setSelected(null);
      setConfirmClear(false);
    } catch (e) {
      addToast(`Clear failed: ${e}`, "error");
    }
  }, [addToast]);

  return (
    <div className="flex h-full gap-3 p-4">
      {/* List pane */}
      <div className="flex flex-col w-80 shrink-0 glass-card p-3 gap-2">
        <div className="flex items-center justify-between">
          <h2 className="font-heading text-[var(--color-gold-bright)] text-base font-semibold flex items-center gap-2">
            <HistoryIcon size={16} /> History
          </h2>
          <div className="flex items-center gap-1">
            <button
              onClick={refresh}
              className="text-[var(--color-text-dim)] hover:text-[var(--color-gold-bright)] p-1 rounded cursor-pointer"
              title="Refresh"
              aria-label="Refresh history"
            >
              <RefreshCw size={14} />
            </button>
            {summaries.length > 0 && !confirmClear && (
              <button
                onClick={() => setConfirmClear(true)}
                className="text-[var(--color-text-dim)] hover:text-[var(--color-danger)] p-1 rounded cursor-pointer"
                title="Clear all"
                aria-label="Clear all history"
              >
                <Trash2 size={14} />
              </button>
            )}
          </div>
        </div>

        {confirmClear && (
          <div className="flex items-center justify-between rounded bg-[var(--color-danger)]/10 border border-[var(--color-danger)]/40 px-2 py-1.5 text-xs">
            <span className="text-[var(--color-danger)]">Delete all {summaries.length}?</span>
            <div className="flex gap-1">
              <button
                onClick={onClearAll}
                className="rounded bg-[var(--color-danger)] text-white px-2 py-0.5 text-[10px] font-semibold cursor-pointer"
              >
                Yes
              </button>
              <button
                onClick={() => setConfirmClear(false)}
                className="rounded bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)] text-[var(--color-text-dim)] px-2 py-0.5 text-[10px] cursor-pointer"
              >
                No
              </button>
            </div>
          </div>
        )}

        <div className="flex items-center gap-1.5 bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] rounded-lg px-2 py-1.5">
          <Search size={12} className="text-[var(--color-text-dim)]" />
          <input
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="Search history…"
            className="flex-1 bg-transparent text-xs text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none"
            aria-label="Search history"
          />
        </div>

        {/* Kind filter chips */}
        <div className="flex flex-wrap gap-1">
          <KindChip
            label="All"
            count={summaries.length}
            active={activeKind === "all"}
            onClick={() => setActiveKind("all")}
          />
          {KIND_ORDER.map((k) => {
            const n = grouped.get(k)?.length ?? 0;
            if (n === 0) return null;
            return (
              <KindChip
                key={k}
                label={KIND_LABELS[k]}
                count={n}
                active={activeKind === k}
                onClick={() => setActiveKind(k)}
              />
            );
          })}
        </div>

        {/* List */}
        <div className="flex-1 overflow-y-auto -mx-1">
          {loading && (
            <div className="flex items-center justify-center py-6 text-[var(--color-text-dim)] text-xs">
              <Loader2 size={14} className="animate-spin mr-2" /> Loading…
            </div>
          )}
          {error && (
            <div className="rounded bg-[var(--color-danger)]/10 border border-[var(--color-danger)]/40 p-2 text-[var(--color-danger)] text-xs">
              {error}
            </div>
          )}
          {!loading && !error && filtered.length === 0 && (
            <div className="flex flex-col items-center justify-center py-8 text-center text-[var(--color-text-dim)] text-xs">
              <ScrollText size={20} className="mb-2 opacity-50" />
              <span>
                {summaries.length === 0
                  ? "Nothing generated yet."
                  : "No matches."}
              </span>
              {summaries.length === 0 && (
                <span className="mt-1 text-[10px]">
                  Every NPC, world, item, and quest you generate will show up here.
                </span>
              )}
            </div>
          )}
          {filtered.map((s) => (
            <button
              key={s.id}
              onClick={() => onSelect(s.id)}
              className={`w-full text-left rounded-lg px-2.5 py-1.5 mb-1 cursor-pointer transition-colors ${
                selected?.id === s.id
                  ? "bg-[var(--color-bg-card)] text-[var(--color-gold-bright)]"
                  : "hover:bg-[var(--color-bg-card)]"
              }`}
            >
              <div className="flex items-center justify-between gap-2">
                <span className="text-[10px] uppercase tracking-wider text-[var(--color-gold-muted)] shrink-0">
                  {KIND_LABELS[s.kind]}
                </span>
                <span className="text-[10px] text-[var(--color-text-dim)] shrink-0">
                  {relativeTime(s.createdAt)}
                </span>
              </div>
              <div className="text-sm text-[var(--color-text-primary)] truncate">
                {s.title}
              </div>
            </button>
          ))}
        </div>
      </div>

      {/* Detail pane */}
      <div className="flex-1 min-w-0 glass-card p-4 overflow-y-auto">
        {selectedLoading ? (
          <div className="flex items-center justify-center h-full text-[var(--color-text-dim)] text-sm">
            <Loader2 size={16} className="animate-spin mr-2" /> Loading…
          </div>
        ) : selected ? (
          <GenerationDetail
            generation={selected}
            onDelete={onDelete}
            onRehydrate={onRehydrate}
          />
        ) : (
          <div className="flex flex-col items-center justify-center h-full text-center text-[var(--color-text-dim)] text-sm">
            <Eye size={28} className="mb-3 opacity-40" />
            <span>Select a generation to view it.</span>
            <span className="mt-1 text-xs">
              From here you can re-open it in its tool or copy as Markdown.
            </span>
          </div>
        )}
      </div>
    </div>
  );
}

function KindChip({
  label,
  count,
  active,
  onClick,
}: {
  label: string;
  count: number;
  active: boolean;
  onClick: () => void;
}) {
  return (
    <button
      onClick={onClick}
      className={`rounded-full px-2 py-0.5 text-[10px] cursor-pointer transition-colors ${
        active
          ? "bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] font-semibold"
          : "bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-secondary)] hover:border-[var(--color-gold-bright)]"
      }`}
    >
      {label} <span className="opacity-70">{count}</span>
    </button>
  );
}

function GenerationDetail({
  generation,
  onDelete,
  onRehydrate,
}: {
  generation: Generation;
  onDelete: (id: number) => void;
  onRehydrate?: (kind: GenerationKind, data: Generation) => void;
}) {
  const { addToast } = useToast();
  const isImage = generation.kind === "image";
  const isSession = generation.kind === "session";

  let parsed: unknown = null;
  if (!isImage) {
    parsed = parseLlmJson(generation.data);
  }

  const fallbackTitle = generation.title;
  const title = isImage
    ? generation.title
    : extractTitle(generation.kind, generation.data, fallbackTitle);

  const rehydratableKinds: GenerationKind[] = [
    "npc",
    "encounter",
    "world",
    "item",
    "quest",
  ];
  const canRehydrate = rehydratableKinds.includes(generation.kind);

  function copyMarkdown() {
    let md: string;
    if (isImage) {
      md = `![${title}](data:image/png;base64,…)\n\n_${new Date(
        generation.createdAt,
      ).toLocaleString()}_`;
    } else if (parsed && typeof parsed === "object") {
      md = `# ${title}\n\n\`\`\`json\n${JSON.stringify(parsed, null, 2)}\n\`\`\``;
    } else {
      md = `# ${title}\n\n${generation.data}`;
    }
    void navigator.clipboard.writeText(md).then(
      () => addToast("Copied as Markdown", "success"),
      () => addToast("Copy failed", "error"),
    );
  }

  return (
    <div className="flex flex-col gap-3">
      {/* Header */}
      <div className="flex items-start justify-between gap-3 pb-2 border-b border-[var(--color-border-glass)]">
        <div className="min-w-0 flex-1">
          <div className="text-[10px] uppercase tracking-wider text-[var(--color-gold-muted)]">
            {KIND_LABELS[generation.kind]}
          </div>
          <h3 className="font-heading text-[var(--color-gold-bright)] text-lg font-bold truncate">
            {title}
          </h3>
          {generation.source && (
            <p className="text-[10px] text-[var(--color-text-dim)] mt-0.5">
              from: {generation.source}
            </p>
          )}
          <p className="text-[10px] text-[var(--color-text-dim)] mt-0.5">
            {new Date(generation.createdAt).toLocaleString()}
          </p>
        </div>
        <div className="flex items-center gap-1 shrink-0">
          {canRehydrate && onRehydrate && (
            <button
              onClick={() => onRehydrate(generation.kind, generation)}
              className="rounded-lg bg-[var(--color-gold-bright)] text-[var(--color-bg-deep)] px-3 py-1.5 text-xs font-semibold hover:bg-[var(--color-gold-muted)] transition-colors cursor-pointer flex items-center gap-1"
              title={`Open in ${KIND_LABELS[generation.kind]} tool`}
            >
              <Sparkles size={12} /> Open in tool
            </button>
          )}
          <button
            onClick={copyMarkdown}
            className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-secondary)] px-3 py-1.5 text-xs hover:border-[var(--color-gold-bright)] hover:text-[var(--color-gold-bright)] transition-colors cursor-pointer"
          >
            Copy
          </button>
          <button
            onClick={() => onDelete(generation.id)}
            className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-dim)] px-2 py-1.5 text-xs hover:text-[var(--color-danger)] transition-colors cursor-pointer"
            title="Delete this generation"
            aria-label="Delete generation"
          >
            <Trash2 size={12} />
          </button>
        </div>
      </div>

      {/* Body */}
      {isImage ? (
        <ImageBody data={generation.data} prompt={generation.title} />
      ) : isSession ? (
        <SessionBody data={generation.data} />
      ) : parsed && typeof parsed === "object" ? (
        <StructuredBody data={parsed} />
      ) : (
        <pre className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)] p-3 text-xs text-[var(--color-text-secondary)] whitespace-pre-wrap break-words">
          {generation.data}
        </pre>
      )}
    </div>
  );
}

function ImageBody({ data, prompt }: { data: string; prompt: string }) {
  return (
    <div className="flex flex-col gap-2 items-start">
      <img
        src={data}
        alt={prompt}
        className="rounded-lg max-w-full max-h-[60vh] border border-[var(--color-border-glass)]"
      />
      <a
        href={data}
        download="dm-pal-image.png"
        className="text-xs text-[var(--color-gold-bright)] hover:text-[var(--color-gold-muted)]"
      >
        ⬇ Save PNG
      </a>
    </div>
  );
}

function SessionBody({ data }: { data: string }) {
  return (
    <div className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] p-3 text-sm text-[var(--color-text-primary)] whitespace-pre-wrap leading-relaxed">
      {data}
    </div>
  );
}

function StructuredBody({ data }: { data: unknown }) {
  if (!data || typeof data !== "object") return null;
  const obj = data as Record<string, unknown>;

  return (
    <div className="flex flex-col gap-3">
      {typeof obj.bio === "string" && (
        <Section label="Bio">
          <p className="text-sm text-[var(--color-text-primary)] leading-relaxed">
            {obj.bio}
          </p>
        </Section>
      )}
      {typeof obj.description === "string" && (
        <Section label="Description">
          <p className="text-sm text-[var(--color-text-primary)] leading-relaxed">
            {obj.description}
          </p>
        </Section>
      )}
      {typeof obj.hook === "string" && (
        <Section label="Hook">
          <p className="text-sm text-[var(--color-text-secondary)] italic">
            {obj.hook}
          </p>
        </Section>
      )}

      {Array.isArray(obj.personality) && obj.personality.length > 0 && (
        <Section label="Personality">
          <div className="flex flex-wrap gap-1.5">
            {ensureArray(obj.personality).map((p, i) => (
              <span
                key={i}
                className="rounded-full bg-[var(--color-gold-glow)] text-[var(--color-gold-bright)] px-2 py-0.5 text-xs"
              >
                {flattenValue(p)}
              </span>
            ))}
          </div>
        </Section>
      )}

      {Array.isArray(obj.goals) && obj.goals.length > 0 && (
        <Section label="Goals">
          <ul className="list-disc list-inside text-sm text-[var(--color-text-primary)] space-y-0.5">
            {ensureArray(obj.goals).map((g, i) => (
              <li key={i}>{flattenValue(g)}</li>
            ))}
          </ul>
        </Section>
      )}

      {Array.isArray(obj.monsters) && obj.monsters.length > 0 && (
        <Section label="Monsters">
          <ul className="list-disc list-inside text-sm text-[var(--color-text-primary)] space-y-0.5">
            {ensureArray(obj.monsters).map((m, i) => (
              <li key={i}>{flattenValue(m)}</li>
            ))}
          </ul>
        </Section>
      )}

      {typeof obj.terrain === "string" && (
        <Section label="Terrain">
          <p className="text-sm text-[var(--color-text-primary)]">{obj.terrain}</p>
        </Section>
      )}

      {typeof obj.difficulty === "string" && (
        <Section label="Difficulty">
          <span className="rounded-full bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-2 py-0.5 text-xs">
            {obj.difficulty}
          </span>
        </Section>
      )}

      {typeof obj.loot === "string" && (
        <Section label="Loot">
          <p className="text-sm text-[var(--color-gold-bright)]">{obj.loot}</p>
        </Section>
      )}

      {Array.isArray(obj.regions) && obj.regions.length > 0 && (
        <Section label="Regions">
          <ul className="text-sm text-[var(--color-text-primary)] space-y-1">
            {ensureArray(obj.regions).map((r, i) => (
              <li
                key={i}
                className="rounded bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)] px-2 py-1"
              >
                {flattenValue(r)}
              </li>
            ))}
          </ul>
        </Section>
      )}

      {Array.isArray(obj.landmarks) && obj.landmarks.length > 0 && (
        <Section label="Landmarks">
          <div className="flex flex-wrap gap-1.5">
            {ensureArray(obj.landmarks).map((l, i) => (
              <span
                key={i}
                className="rounded-full bg-[var(--color-gold-glow)] text-[var(--color-gold-bright)] px-2 py-0.5 text-xs"
              >
                {flattenValue(l)}
              </span>
            ))}
          </div>
        </Section>
      )}

      {Array.isArray(obj.conflicts) && obj.conflicts.length > 0 && (
        <Section label="Conflicts">
          <div className="flex flex-col gap-1">
            {ensureArray(obj.conflicts).map((c, i) => (
              <div
                key={i}
                className="rounded bg-[var(--color-bg-surface)] border-l-2 border-[var(--color-danger)] px-2 py-1 text-sm text-[var(--color-text-primary)]"
              >
                {flattenValue(c)}
              </div>
            ))}
          </div>
        </Section>
      )}

      {Array.isArray(obj.cultures) && obj.cultures.length > 0 && (
        <Section label="Cultures">
          <div className="flex flex-wrap gap-1.5">
            {ensureArray(obj.cultures).map((c, i) => (
              <span
                key={i}
                className="rounded-full bg-[var(--color-bg-surface)] border border-[var(--color-info)]/30 text-[var(--color-info)] px-2 py-0.5 text-xs"
              >
                {flattenValue(c)}
              </span>
            ))}
          </div>
        </Section>
      )}

      {Array.isArray(obj.steps) && obj.steps.length > 0 && (
        <Section label="Steps">
          <ol className="flex flex-col gap-2 list-decimal list-inside">
            {ensureArray(obj.steps).map((s, i) => {
              const step = typeof s === "object" && s !== null
                ? (s as Record<string, unknown>)
                : { title: "", description: flattenValue(s) };
              return (
                <li
                  key={i}
                  className="rounded bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)] px-3 py-2"
                >
                  {typeof step.title === "string" && (
                    <div className="text-sm font-semibold text-[var(--color-gold-bright)]">
                      {step.title}
                    </div>
                  )}
                  {typeof step.description === "string" && (
                    <div className="text-sm text-[var(--color-text-primary)] mt-0.5">
                      {step.description}
                    </div>
                  )}
                </li>
              );
            })}
          </ol>
        </Section>
      )}

      {typeof obj.twist === "string" && (
        <Section label="Twist">
          <div className="rounded border-l-2 border-[var(--color-danger)] bg-[var(--color-bg-surface)] px-3 py-2 text-sm text-[var(--color-text-primary)]">
            {obj.twist}
          </div>
        </Section>
      )}

      {typeof obj.reward === "string" && (
        <Section label="Reward">
          <div className="rounded border-l-2 border-[var(--color-gold-bright)] bg-[var(--color-bg-surface)] px-3 py-2 text-sm text-[var(--color-text-primary)]">
            {obj.reward}
          </div>
        </Section>
      )}

      {typeof obj.mechanical === "string" && (
        <Section label="Mechanics">
          <div className="rounded bg-[var(--color-bg-deep)] border border-[var(--color-border-glass)] p-2 text-sm text-[var(--color-text-primary)]">
            {obj.mechanical}
          </div>
        </Section>
      )}

      {typeof obj.lore === "string" && (
        <Section label="Lore">
          <p className="text-sm text-[var(--color-text-secondary)] italic">
            {obj.lore}
          </p>
        </Section>
      )}

      {typeof obj.rarity === "string" && (
        <Section label="Rarity">
          <span className="text-xs text-[var(--color-text-secondary)]">
            {obj.rarity}
            {typeof obj.type === "string" && <> · {obj.type}</>}
          </span>
        </Section>
      )}

      {/* Fallback: any keys we didn't render show as a key/value table. */}
      <details className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-subtle)] mt-2">
        <summary className="px-3 py-2 text-xs text-[var(--color-text-dim)] cursor-pointer hover:text-[var(--color-text-secondary)]">
          Raw JSON
        </summary>
        <pre className="px-3 pb-3 text-xs text-[var(--color-text-secondary)] overflow-x-auto whitespace-pre-wrap break-words">
          {JSON.stringify(data, null, 2)}
        </pre>
      </details>
    </div>
  );
}

function Section({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="flex flex-col gap-1">
      <span className="text-[10px] uppercase tracking-wider text-[var(--color-text-dim)] font-medium">
        {label}
      </span>
      {children}
    </div>
  );
}

// ─── Modal wrapper ────────────────────────────────────────────

interface HistoryDrawerProps {
  open: boolean;
  onClose: () => void;
  onRehydrate?: (kind: GenerationKind, data: Generation) => void;
}

export function HistoryDrawer({ open, onClose, onRehydrate }: HistoryDrawerProps) {
  return (
    <AnimatePresence>
      {open && (
        <motion.div
          className="fixed inset-0 z-30 bg-black/50"
          onClick={onClose}
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          aria-modal="true"
          role="dialog"
          aria-label="Generation history"
        >
          <motion.div
            className="absolute inset-4 lg:inset-8 bg-[var(--color-bg-deep)] rounded-2xl border border-[var(--color-border-glass)] overflow-hidden"
            onClick={(e) => e.stopPropagation()}
            initial={{ scale: 0.97, opacity: 0 }}
            animate={{ scale: 1, opacity: 1 }}
            exit={{ scale: 0.97, opacity: 0 }}
            transition={{ type: "spring", stiffness: 300, damping: 30 }}
          >
            <div className="flex items-center justify-between px-4 py-2 border-b border-[var(--color-border-subtle)] bg-[var(--color-bg-surface)]">
              <h2 className="font-heading text-[var(--color-gold-bright)] text-sm font-semibold flex items-center gap-2">
                <HistoryIcon size={14} /> Generation History
              </h2>
              <button
                onClick={onClose}
                className="text-[var(--color-text-dim)] hover:text-[var(--color-text-primary)] cursor-pointer"
                title="Close (Esc)"
                aria-label="Close history"
              >
                <X size={18} />
              </button>
            </div>
            <div className="h-[calc(100%-44px)]">
              <HistoryView onRehydrate={(k, d) => {
                onRehydrate?.(k, d);
                onClose();
              }} />
            </div>
          </motion.div>
        </motion.div>
      )}
    </AnimatePresence>
  );
}
