import { useState, useEffect, useMemo } from "react";
import { invoke, Channel } from "@tauri-apps/api/core";
import { addToLore } from "../lib/lore";
import { useToast } from "./Toast";
import { addGeneration, type Generation } from "../lib/generations";
import { usePrefillEffect } from "../lib/usePrefill";
import { usePersistentState } from "../lib/usePersistentState";
import { renderMarkdown } from "../lib/markdown";

interface Props {
  prefill?: Generation | null;
  onPrefillConsumed?: () => void;
}

interface SessionEntry {
  text: string;
  time: string;
  tag?: string;
}

interface Session {
  id: string;
  title: string;
  entries: SessionEntry[];
  summary: string;
  createdAt: number;
}

const TAGS = ["#combat", "#roleplay", "#loot", "#quest", "#npc", "#misc"];

function newSession(title: string): Session {
  return { id: crypto.randomUUID(), title, entries: [], summary: "", createdAt: Date.now() };
}

export function SessionLogger({ prefill, onPrefillConsumed }: Props = {}) {
  // ponytail: multiple sessions as a first-class concept. The whole roster
  // persists under one key; the active session id is stored separately so a
  // reload lands on the session the DM was last editing.
  const [sessions, setSessions] = usePersistentState<Session[]>("session.list", [newSession("Session 1")]);
  const [activeId, setActiveId] = usePersistentState<string>("session.active", "");
  const [notes, setNotes] = useState("");
  const [tag, setTag] = useState(TAGS[0]);
  const [loading, setLoading] = useState(false);
  const [gotFirstToken, setGotFirstToken] = useState(false);
  const [filterTag, setFilterTag] = useState<string | null>(null);
  const [previewSummary, setPreviewSummary] = useState(true);
  const [renaming, setRenaming] = useState(false);
  const { addToast } = useToast();

  // ponytail: ensure an active session exists (first run / migration).
  useEffect(() => {
    if (sessions.length === 0) {
      const s = newSession("Session 1");
      setSessions([s]);
      setActiveId(s.id);
    } else if (!sessions.some((s) => s.id === activeId) && activeId !== "") {
      setActiveId(sessions[0].id);
    } else if (activeId === "") {
      setActiveId(sessions[0].id);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [sessions.length]);

  const active = useMemo(
    () => sessions.find((s) => s.id === activeId) ?? sessions[0],
    [sessions, activeId],
  );

  // ponytail: helpers that update the active session within the roster.
  function updateActive(updater: (s: Session) => Session) {
    setSessions((prev) => prev.map((s) => (s.id === active?.id ? updater(s) : s)));
  }

  usePrefillEffect(prefill ?? null, "session", () => onPrefillConsumed?.(), (g) => {
    if (!active) return;
    updateActive((s) => ({ ...s, summary: g.data }));
    addToast(`Loaded "${g.title}" from history`, "info");
  });

  function addEntry() {
    if (!notes.trim() || !active) return;
    updateActive((s) => ({
      ...s,
      entries: [{ text: notes.trim(), time: new Date().toLocaleString(), tag }, ...s.entries],
    }));
    setNotes("");
  }

  function removeEntry(i: number) {
    updateActive((s) => ({ ...s, entries: s.entries.filter((_, j) => j !== i) }));
  }

  function addSession() {
    const s = newSession(`Session ${sessions.length + 1}`);
    setSessions((prev) => [...prev, s]);
    setActiveId(s.id);
    setNotes("");
    setFilterTag(null);
  }

  function deleteSession() {
    if (sessions.length <= 1) {
      addToast("Keep at least one session", "info");
      return;
    }
    const idx = sessions.findIndex((s) => s.id === active?.id);
    const next = sessions.filter((s) => s.id !== active?.id);
    setSessions(next);
    setActiveId(next[Math.max(0, idx - 1)].id);
    addToast("Session deleted", "info");
  }

  function exportMarkdown() {
    if (!active || active.entries.length === 0) return;
    const lines = [...active.entries].reverse().map((e) => `- [${e.time}] ${e.tag ? `${e.tag} ` : ""}${e.text}`);
    const md = `# ${active.title} (${new Date(active.createdAt).toLocaleDateString()})\n\n## Notes\n\n${lines.join("\n")}\n\n${active.summary ? `## AI Summary\n\n${active.summary}` : ""}`;
    const blob = new Blob([md], { type: "text/markdown" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${active.title.toLowerCase().replace(/\s+/g, "-")}-${new Date().toISOString().slice(0, 10)}.md`;
    a.click();
    URL.revokeObjectURL(url);
    addToast("Exported session to Markdown", "success");
  }

  async function summarize() {
    if (!active || active.entries.length === 0) return;
    setLoading(true);
    updateActive((s) => ({ ...s, summary: "" }));
    setGotFirstToken(false);
    const sessionText = active.entries.map((e) => `[${e.time}] ${e.tag ? `${e.tag} ` : ""}${e.text}`).join("\n");
    const channel = new Channel<{ type: string; data: string }>();
    let streamed = "";
    channel.onmessage = (event) => {
      if (event.type === "token") {
        setGotFirstToken(true);
        streamed = streamed + event.data;
        updateActive((s) => ({ ...s, summary: streamed }));
      } else if (event.type === "done") {
        setGotFirstToken(true);
        streamed = event.data;
        updateActive((s) => ({ ...s, summary: streamed }));
      } else if (event.type === "error") {
        addToast(`Summary failed: ${event.data}`, "error");
      }
    };
    try {
      await invoke("generate_stream", {
        req: {
          prompt: `Summarize the following D&D session notes and suggest 2-3 hooks for the next session:\n\n${sessionText}`,
          system: "You are a helpful D&D session summarizer. Be concise and creative.",
          temperature: 0.7,
          max_tokens: 512,
        },
        channel,
      });
      addToast("Session summary generated", "success");
      const result = streamed;
      const title = active.title;
      void addGeneration({ kind: "session", title, data: result, source: `${active.entries.length} notes` });
      void addToLore(`Session: ${title}`, `Session summary:\n${result}\n\nNotes:\n${sessionText}`);
    } catch (e) {
      addToast(`Summary failed: ${e}`, "error");
    }
    setLoading(false);
  }

  if (!active) return null;

  const visibleEntries = filterTag === null ? active.entries : active.entries.filter((e) => e.tag === filterTag);
  const usedTags = TAGS.filter((t) => active.entries.some((e) => e.tag === t));

  return (
    <div className="flex flex-col gap-2 h-full">
      {/* Session switcher */}
      <div className="flex items-center gap-1.5">
        {renaming ? (
          <input
            autoFocus
            className="flex-1 min-w-0 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-2 py-1 text-[var(--color-text-primary)] focus:outline-none focus:border-[var(--color-gold-bright)] text-xs"
            value={active.title}
            onChange={(e) => updateActive((s) => ({ ...s, title: e.target.value }))}
            onBlur={() => setRenaming(false)}
            onKeyDown={(e) => e.key === "Enter" && setRenaming(false)}
          />
        ) : (
          <select
            value={active.id}
            onChange={(e) => { setActiveId(e.target.value); setFilterTag(null); setNotes(""); }}
            className="flex-1 min-w-0 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-2 py-1 text-[var(--color-text-primary)] focus:outline-none focus:border-[var(--color-gold-bright)] text-xs cursor-pointer"
            aria-label="Switch session"
          >
            {sessions.map((s) => (
              <option key={s.id} value={s.id}>
                {s.title} ({s.entries.length})
              </option>
            ))}
          </select>
        )}
        <button
          onClick={() => setRenaming((r) => !r)}
          className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-dim)] px-2 py-1 text-xs hover:text-[var(--color-gold-bright)] hover:border-[var(--color-gold-bright)] cursor-pointer"
          title="Rename session"
        >
          ✎
        </button>
        <button
          onClick={addSession}
          className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-dim)] px-2 py-1 text-xs hover:text-[var(--color-gold-bright)] hover:border-[var(--color-gold-bright)] cursor-pointer"
          title="New session"
        >
          + New
        </button>
        <button
          onClick={deleteSession}
          className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-dim)] px-2 py-1 text-xs hover:text-[var(--color-danger)] hover:border-[var(--color-danger)] cursor-pointer"
          title="Delete this session"
        >
          🗑
        </button>
      </div>

      <div className="flex gap-3 flex-1 min-h-0">
        {/* Notes column */}
        <div className="flex flex-col gap-2 flex-1 min-h-0">
          <textarea
            className="flex-1 min-h-0 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] p-3 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)] text-sm resize-none"
            value={notes}
            onChange={(e) => setNotes(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === "Enter" && !e.shiftKey) {
                e.preventDefault();
                addEntry();
              }
            }}
            placeholder="Type a session note, press Enter to add…"
          />
          <div className="flex items-center gap-2 flex-wrap">
            <select
              value={tag}
              onChange={(e) => setTag(e.target.value)}
              className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-primary)] focus:outline-none focus:border-[var(--color-gold-bright)] text-xs cursor-pointer"
              aria-label="Tag for new note"
            >
              {TAGS.map((t) => <option key={t} value={t}>{t}</option>)}
            </select>
            <button
              onClick={addEntry}
              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"
            >
              Add Note
            </button>
            <button
              onClick={summarize}
              disabled={loading || active.entries.length === 0}
              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 disabled:opacity-50"
            >
              {loading ? "Summarizing…" : "✨ AI Summary"}
            </button>
            <button
              onClick={exportMarkdown}
              disabled={active.entries.length === 0}
              className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] text-[var(--color-text-dim)] px-3 py-1.5 text-xs hover:text-[var(--color-gold-bright)] hover:border-[var(--color-gold-bright)] transition-colors cursor-pointer disabled:opacity-50"
              title="Export notes + summary to a Markdown file"
            >
              ⬇ Export
            </button>
          </div>
          <div className="flex-1 min-h-0 overflow-y-auto">
            {/* ponytail: tag filter — click a chip to filter, click again to clear. */}
            {usedTags.length > 0 && (
              <div className="flex flex-wrap gap-1 mb-1">
                <button
                  onClick={() => setFilterTag(null)}
                  className={`rounded-full px-2 py-0.5 text-[10px] border cursor-pointer transition-colors ${
                    filterTag === null
                      ? "bg-[var(--color-gold-glow)] border-[var(--color-gold-bright)] text-[var(--color-gold-bright)]"
                      : "bg-[var(--color-bg-deep)] border-[var(--color-border-subtle)] text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)]"
                  }`}
                >
                  all
                </button>
                {usedTags.map((t) => (
                  <button
                    key={t}
                    onClick={() => setFilterTag((f) => (f === t ? null : t))}
                    className={`rounded-full px-2 py-0.5 text-[10px] border cursor-pointer transition-colors ${
                      filterTag === t
                        ? "bg-[var(--color-gold-glow)] border-[var(--color-gold-bright)] text-[var(--color-gold-bright)]"
                        : "bg-[var(--color-bg-deep)] border-[var(--color-border-subtle)] text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)]"
                    }`}
                  >
                    {t}
                  </button>
                ))}
              </div>
            )}
            {visibleEntries.map((e, i) => (
              <div key={i} className="flex gap-2 py-1 border-b border-[var(--color-border-subtle)] group">
                <span className="text-[var(--color-text-dim)] text-xs font-mono shrink-0">
                  {e.time}
                </span>
                {e.tag && <span className="text-[var(--color-gold-bright)] text-[10px] shrink-0">{e.tag}</span>}
                <span className="text-[var(--color-text-primary)] text-xs flex-1">{e.text}</span>
                <button
                  onClick={() => removeEntry(active.entries.indexOf(e))}
                  className="text-[var(--color-text-dim)] hover:text-[var(--color-danger)] text-[10px] cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity"
                  title="Delete entry"
                >
                  ×
                </button>
              </div>
            ))}
          </div>
        </div>

        {/* AI Summary column */}
        <div className="flex-1 flex flex-col min-h-0">
          <div className="flex items-center justify-between mb-2">
            <h3 className="text-xs font-medium text-[var(--color-text-secondary)]">AI Summary</h3>
            {active.summary ? (
              <button
                onClick={() => setPreviewSummary((p) => !p)}
                className={`rounded px-2 py-0.5 text-[10px] border cursor-pointer transition-colors ${
                  previewSummary
                    ? "bg-[var(--color-gold-glow)] border-[var(--color-gold-bright)] text-[var(--color-gold-bright)]"
                    : "bg-[var(--color-bg-deep)] border-[var(--color-border-subtle)] text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)]"
                }`}
                title="Toggle Markdown preview"
              >
                {previewSummary ? "Preview" : "Raw"}
              </button>
            ) : null}
          </div>
          <div className="flex-1 min-h-0 rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] p-3 text-[var(--color-text-primary)] text-sm overflow-y-auto">
            {loading && !gotFirstToken ? (
              <span className="text-[var(--color-gold-bright)] tracking-widest">
                <span className="thinking-dot">•</span>
                <span className="thinking-dot">•</span>
                <span className="thinking-dot">•</span>
              </span>
            ) : active.summary ? (
              previewSummary ? (
                <div className="md-preview" dangerouslySetInnerHTML={{ __html: renderMarkdown(active.summary) }} />
              ) : (
                <span className="whitespace-pre-wrap">{active.summary}{loading && <span className="text-[var(--color-gold-bright)]">▍</span>}</span>
              )
            ) : (
              <span className="text-[var(--color-text-dim)] italic">
                Add session notes and click "AI Summary" to generate a recap and next-session hooks.
              </span>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}