"use client";

import { useState, useRef, useCallback } from "react";
import type { Stats, RawNote, Period, RunPayload } from "./_client/types";
import { supportsFSA, readViaFSA, readViaInput, readViaDrop } from "./_client/reader";

type Screen = "intro" | "loading" | "result" | "error";
type View = "card" | "graph";

const ARCHETYPE_RU: Record<string, string> = {
  architect: "🏛 Архитектор",
  gardener: "🌱 Садовник",
  librarian: "📚 Библиотекарь",
  pragmatist: "⚡ Прагматик",
};
const PERIOD_RU: Record<Period, string> = { year: "год", quarter: "квартал", all: "всё время" };

export function WrappedClient({ userName }: { userName: string }) {
  const [screen, setScreen] = useState<Screen>("intro");
  const [stats, setStats] = useState<Stats | null>(null);
  const [period, setPeriod] = useState<Period>("year");
  const [error, setError] = useState("");
  const [shareUrl, setShareUrl] = useState("");
  const [sharing, setSharing] = useState(false);
  const [view, setView] = useState<View>("card");
  const [graphUrl, setGraphUrl] = useState("");
  const [graphLabels, setGraphLabels] = useState(false);
  const [graphBusy, setGraphBusy] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);
  const notesRef = useRef<RawNote[]>([]);

  // Сбросить отрисованный граф (при новых данных / смене периода).
  const resetGraph = useCallback(() => {
    setView("card");
    setGraphUrl((prev) => {
      if (prev) URL.revokeObjectURL(prev);
      return "";
    });
  }, []);

  // Отрисовать граф-постер в офф-скрин Canvas → blob URL. force-graph грузим лениво.
  const renderGraph = useCallback(
    async (labels: boolean) => {
      if (!stats || !stats.graph.nodes.length) return;
      setGraphBusy(true);
      setError("");
      try {
        const { renderGraphPoster } = await import("./_client/graphPoster");
        const blob = await renderGraphPoster(stats.graph, labels);
        setGraphUrl((prev) => {
          if (prev) URL.revokeObjectURL(prev);
          return URL.createObjectURL(blob);
        });
      } catch {
        setError("Не удалось построить граф");
      }
      setGraphBusy(false);
    },
    [stats],
  );

  const runWorker = useCallback((notes: RawNote[], p: Period) => {
    setScreen("loading");
    const worker = new Worker(new URL("./_client/parse.worker.ts", import.meta.url), { type: "module" });
    worker.onmessage = (e: MessageEvent<Stats>) => {
      setStats(e.data);
      resetGraph();
      setScreen("result");
      worker.terminate();
    };
    worker.onerror = () => {
      setError("Не удалось обработать хранилище");
      setScreen("error");
      worker.terminate();
    };
    worker.postMessage({ notes, period: p });
  }, [resetGraph]);

  const handleNotes = useCallback(
    (notes: RawNote[]) => {
      if (!notes.length) {
        setError("В выбранной папке не нашлось .md заметок");
        setScreen("error");
        return;
      }
      notesRef.current = notes;
      runWorker(notes, period);
    },
    [period, runWorker],
  );

  async function pickFSA() {
    try {
      handleNotes(await readViaFSA());
    } catch (e) {
      if ((e as Error).name !== "AbortError") {
        setError("Не удалось прочитать папку");
        setScreen("error");
      }
    }
  }

  async function onInput(e: React.ChangeEvent<HTMLInputElement>) {
    if (e.target.files) handleNotes(await readViaInput(e.target.files));
  }

  async function onDrop(e: React.DragEvent) {
    e.preventDefault();
    if (e.dataTransfer.items) handleNotes(await readViaDrop(e.dataTransfer.items));
  }

  function changePeriod(p: Period) {
    setPeriod(p);
    if (notesRef.current.length) runWorker(notesRef.current, p);
  }

  async function share() {
    if (!stats || sharing) return;
    setSharing(true);
    setError("");
    const payload: RunPayload = {
      period: stats.period,
      noteCount: stats.noteCount,
      linkCount: stats.linkCount,
      tagCount: stats.tagCount,
      topTag: stats.topTags[0]?.tag ?? null,
      archetype: stats.archetype,
      score: stats.score,
    };
    try {
      const res = await fetch("/api/wrapped/run", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify(payload),
      });
      const data = (await res.json()) as { shareToken?: string };
      if (data.shareToken) setShareUrl(`${location.origin}/share/wrapped/${data.shareToken}`);
      else setError("Не удалось создать ссылку");
    } catch {
      setError("Не удалось создать ссылку");
    }
    setSharing(false);
  }

  return (
    <main className="max-w-2xl mx-auto px-6 py-12 w-full">
      <p className="text-xs font-bold uppercase tracking-widest mb-2" style={{ color: "var(--muted-foreground)" }}>
        Obsidian Wrapped · 2026
      </p>

      {screen === "intro" && (
        <div className="card-aubade p-8">
          <h1 className="text-2xl font-bold mb-3">Твой год в заметках, {userName.split(" ")[0]}</h1>
          <p className="text-sm mb-6" style={{ color: "var(--muted-foreground)" }}>
            Выбери папку своего хранилища — всё считается прямо в браузере, заметки никуда не загружаются.
            На выходе — карточка «твой год в заметках», которой можно поделиться.
          </p>
          <div
            onDrop={onDrop}
            onDragOver={(e) => e.preventDefault()}
            className="p-8 text-center mb-4"
            style={{ border: "2px dashed var(--border)", borderRadius: 2 }}
          >
            <p className="text-3xl mb-2">📂</p>
            {supportsFSA() ? (
              <button onClick={pickFSA} className="btn-aubade btn-aubade-accent text-sm">
                Выбрать папку хранилища
              </button>
            ) : (
              <button onClick={() => inputRef.current?.click()} className="btn-aubade btn-aubade-accent text-sm">
                Выбрать папку хранилища
              </button>
            )}
            <p className="text-xs mt-3" style={{ color: "var(--muted-foreground)" }}>
              или перетащи папку сюда
            </p>
            <input
              ref={inputRef}
              type="file"
              // @ts-expect-error webkitdirectory не в типах
              webkitdirectory=""
              directory=""
              multiple
              hidden
              onChange={onInput}
            />
          </div>
          <p className="text-xs" style={{ color: "var(--muted-foreground)" }}>
            🔒 Приватность: тексты заметок остаются в браузере. На сервер уходят только числа (сколько заметок, связей, тегов).
          </p>
        </div>
      )}

      {screen === "loading" && (
        <div className="card-aubade p-12 text-center">
          <p className="text-3xl mb-3">⏳</p>
          <p className="font-medium">Считаю твой год…</p>
        </div>
      )}

      {screen === "error" && (
        <div className="card-aubade p-8 text-center">
          <p className="text-sm mb-4" style={{ color: "oklch(0.577 0.245 27.325)" }}>{error}</p>
          <button onClick={() => { setScreen("intro"); setError(""); }} className="btn-aubade text-sm">
            Попробовать снова
          </button>
        </div>
      )}

      {screen === "result" && stats && (
        <div className="space-y-4">
          {/* переключатель периода */}
          <div className="flex flex-wrap items-center gap-2">
            {(["year", "quarter", "all"] as Period[]).map((p) => (
              <button
                key={p}
                onClick={() => changePeriod(p)}
                className="text-xs px-3 py-1.5"
                style={{
                  border: "2px solid var(--foreground)",
                  borderRadius: 2,
                  background: period === p ? "var(--accent)" : "var(--background)",
                  fontWeight: period === p ? 700 : 400,
                }}
              >
                {PERIOD_RU[p]}
              </button>
            ))}
            {/* переключатель вида: карточка / граф */}
            <div className="flex gap-2 ml-auto">
              {(["card", "graph"] as View[]).map((v) => (
                <button
                  key={v}
                  onClick={() => {
                    setView(v);
                    if (v === "graph" && !graphUrl && !graphBusy) renderGraph(graphLabels);
                  }}
                  className="text-xs px-3 py-1.5"
                  style={{
                    border: "2px solid var(--foreground)",
                    borderRadius: 2,
                    background: view === v ? "var(--accent)" : "var(--background)",
                    fontWeight: view === v ? 700 : 400,
                  }}
                >
                  {v === "card" ? "Карточка" : "Граф"}
                </button>
              ))}
            </div>
          </div>

          {/* граф-постер хранилища */}
          {view === "graph" && (
            <div className="card-aubade p-6 space-y-3">
              <div className="flex items-center justify-between gap-3">
                <h2 className="font-bold text-xl">Граф твоего хранилища</h2>
                <label className="text-xs flex items-center gap-1.5" style={{ color: "var(--muted-foreground)" }}>
                  <input
                    type="checkbox"
                    checked={graphLabels}
                    onChange={(e) => { setGraphLabels(e.target.checked); renderGraph(e.target.checked); }}
                  />
                  подписи узлов
                </label>
              </div>
              <p className="text-xs" style={{ color: "var(--muted-foreground)" }}>
                {stats.graph.nodes.length} самых связанных заметок из твоего хранилища. Имена остаются в браузере.
              </p>
              <div className="flex items-center justify-center" style={{ border: "2px solid var(--border)", borderRadius: 2, minHeight: 280, background: "#F5F5F0" }}>
                {graphBusy ? (
                  <p className="text-sm py-12" style={{ color: "var(--muted-foreground)" }}>Строю граф…</p>
                ) : graphUrl ? (
                  // eslint-disable-next-line @next/next/no-img-element
                  <img src={graphUrl} alt="Граф хранилища" className="w-full h-auto" style={{ borderRadius: 2 }} />
                ) : stats.graph.nodes.length === 0 ? (
                  <p className="text-sm py-12 px-4 text-center" style={{ color: "var(--muted-foreground)" }}>
                    Слишком мало связей между заметками, чтобы построить граф.
                  </p>
                ) : null}
              </div>
              {graphUrl && (
                <a href={graphUrl} download="obsidian-graph.png" className="btn-aubade text-sm inline-block">
                  Скачать граф (PNG)
                </a>
              )}
            </div>
          )}

          {/* DOM-карточка результата (ДС-2) */}
          {view === "card" && (
          <div className="card-aubade p-6">
            <h2 className="font-bold text-xl mb-1">Ты — {ARCHETYPE_RU[stats.archetype]}</h2>
            <p className="text-xs uppercase tracking-widest mb-4" style={{ color: "var(--muted-foreground)" }}>
              зрелость {stats.score}/100 · период: {PERIOD_RU[stats.period]}
            </p>
            <div className="grid grid-cols-3 gap-3 mb-4">
              {[
                { n: stats.noteCount, l: "заметок" },
                { n: stats.linkCount, l: "связей" },
                { n: stats.tagCount, l: "тегов" },
              ].map((m) => (
                <div key={m.l} className="p-3 text-center" style={{ border: "2px solid var(--border)", boxShadow: "4px 4px 0 0 var(--border)" }}>
                  <p className="text-2xl font-bold">{m.n}</p>
                  <p className="text-xs" style={{ color: "var(--muted-foreground)" }}>{m.l}</p>
                </div>
              ))}
            </div>
            {stats.topTags.length > 0 && (
              <p className="text-sm mb-2">
                <span className="text-xs uppercase tracking-widest" style={{ color: "var(--muted-foreground)" }}>Топ-теги: </span>
                {stats.topTags.map((t) => `#${t.tag}`).join("  ")}
              </p>
            )}
            {stats.achievements.length > 0 && (
              <div className="flex flex-wrap gap-2 mt-3">
                {stats.achievements.map((a) => (
                  <span key={a} className="text-xs px-2 py-1" style={{ background: "var(--accent)", border: "2px solid var(--foreground)", borderRadius: 2 }}>
                    {a}
                  </span>
                ))}
              </div>
            )}
          </div>
          )}

          {/* действия */}
          <div className="flex flex-col sm:flex-row gap-3">
            <button onClick={share} disabled={sharing} className="btn-aubade btn-aubade-accent text-sm flex-1">
              {sharing ? "Создаю ссылку…" : "Поделиться карточкой"}
            </button>
            <button onClick={() => { setScreen("intro"); setStats(null); setShareUrl(""); resetGraph(); }} className="btn-aubade text-sm flex-1">
              Другое хранилище
            </button>
          </div>

          {shareUrl && (
            <div className="card-aubade p-4 space-y-3">
              <div>
                <p className="text-xs uppercase tracking-widest mb-2" style={{ color: "var(--muted-foreground)" }}>Публичная ссылка</p>
                <a href={shareUrl} target="_blank" rel="noopener noreferrer" className="text-sm underline break-all" style={{ color: "var(--foreground)" }}>
                  {shareUrl}
                </a>
              </div>
              <a href={`${shareUrl}/opengraph-image`} download className="btn-aubade text-sm inline-block">
                Скачать картинку (PNG)
              </a>
            </div>
          )}
          {error && <p className="text-xs" style={{ color: "oklch(0.577 0.245 27.325)" }}>{error}</p>}
        </div>
      )}
    </main>
  );
}
