import { useState, useRef, useEffect } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Chip from "@mui/material/Chip";
import CircularProgress from "@mui/material/CircularProgress";
import { Card, Button, TextField } from "../../ui";
import apiFetch from "../../utils/api";
import { useAlert } from "../../context/AlertContext";
import {
  aiUsageOptions,
  aiHistoryOptions,
  type ExtractedInvoice,
} from "../../lib/queries/ai";

interface ChatTurn {
  role: "user" | "assistant";
  content: string;
}
// The review card is a *form* model: every editable field is a string (what the
// inputs hold), distinct from the API's ExtractedInvoice. We convert numbers at
// save time. This keeps the type honest — no `number` field holding a string.
interface ReviewInvoice {
  uid: string;
  supplier_name: string;
  invoice_number: string;
  amount: string;
  currency: string;
  vat_rate: string;
  issue_date: string;
  due_date: string;
  description: string;
  file_name: string;
  file: File;
}
type EditableField =
  | "supplier_name"
  | "invoice_number"
  | "amount"
  | "currency"
  | "vat_rate"
  | "issue_date"
  | "due_date"
  | "description";

// A staged file carries a stable id so chip keys survive mid-list removal.
interface StagedFile {
  id: string;
  file: File;
}

// Stable, monotonic id. NOT crypto.randomUUID() — that only exists in a secure
// context (HTTPS/localhost) and throws over plain HTTP on a LAN. A counter is
// unique within the session and context-independent.
let uidSeq = 0;
const nextUid = () => `ai-${(uidSeq += 1)}`;

// How many recent turns to send the model as context (bounds tokens/cost).
const MODEL_CONTEXT = 20;
// Server schema caps a stored message at 8000 chars.
const MAX_STORE = 8000;

const plural = (n: number) =>
  n === 1 ? "fakturu" : n >= 2 && n <= 4 ? "faktury" : "faktur";

/** A human, Czech summary of an extraction so the assistant "reports back". */
function summaryNote(reviews: ReviewInvoice[]): string {
  if (reviews.length === 0) return "V přílohách jsem nenašel žádnou fakturu.";
  const suppliers = [
    ...new Set(reviews.map((r) => r.supplier_name).filter(Boolean)),
  ];
  const supTxt =
    suppliers.length === 1
      ? ` od ${suppliers[0]}`
      : suppliers.length > 1
        ? ` od ${suppliers[0]} a dalších`
        : "";
  const totals: Record<string, number> = {};
  for (const r of reviews) {
    const cur = r.currency || "CZK";
    totals[cur] = (totals[cur] || 0) + (Number(r.amount) || 0);
  }
  const totalsTxt = Object.entries(totals)
    .map(
      ([cur, sum]) =>
        `${sum.toLocaleString("cs-CZ", {
          minimumFractionDigits: 2,
          maximumFractionDigits: 2,
        })} ${cur}`,
    )
    .join(" + ");
  return `Načetl jsem ${reviews.length} ${plural(reviews.length)}${supTxt}, celkem ${totalsTxt}. Zkontrolujte údaje a uložte.`;
}

export default function DashAssistant() {
  const alert = useAlert();
  const qc = useQueryClient();
  const { data: usage } = useQuery(aiUsageOptions());
  const { data: historyData, isPending: historyPending } =
    useQuery(aiHistoryOptions());

  const [turns, setTurns] = useState<ChatTurn[]>([]);
  const [input, setInput] = useState("");
  const [busy, setBusy] = useState(false);
  const [review, setReview] = useState<ReviewInvoice[]>([]);
  const [attachments, setAttachments] = useState<StagedFile[]>([]);
  const fileRef = useRef<HTMLInputElement>(null);
  const threadRef = useRef<HTMLDivElement>(null);
  const seeded = useRef(false);

  // Seed the thread from server history exactly once (first time it arrives).
  useEffect(() => {
    if (!seeded.current && historyData) {
      seeded.current = true;
      setTurns(
        historyData.messages.map((m) => ({ role: m.role, content: m.content })),
      );
    }
  }, [historyData]);

  // Keep the thread scrolled to the latest message. Deliberately NOT keyed on
  // `review` — editing a review-card field must not yank the thread to bottom.
  useEffect(() => {
    const el = threadRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [turns, busy]);

  // AI is hidden entirely when the backend isn't configured.
  if (usage && usage.configured === false) return null;

  // Best-effort: persist the given turns to the server thread. A persistence
  // blip must not fail the user's interaction, but must not be swallowed.
  const persist = (msgs: ChatTurn[]) => {
    apiFetch("/api/admin/ai/history", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        messages: msgs.map((m) => ({
          role: m.role,
          content: m.content.slice(0, MAX_STORE),
        })),
      }),
    })
      .then((r) => {
        if (!r.ok) return r.json().then((b) => Promise.reject(b?.error));
      })
      .catch((e) => console.error("[ai] history persist failed", e));
  };

  // Attaching only *stages* files — nothing is sent until the user presses
  // Odeslat. We append (multiple picks accumulate) and clear the input element.
  const onFiles = (files: FileList | null) => {
    if (!files || files.length === 0) return;
    setAttachments((a) => [
      ...a,
      ...Array.from(files).map((file) => ({ id: nextUid(), file })),
    ]);
    if (fileRef.current) fileRef.current.value = "";
  };

  const removeAttachment = (id: string) =>
    setAttachments((a) => a.filter((s) => s.id !== id));

  // Disabled until history has settled, so the user can't send a turn before
  // the seed runs (which would otherwise let a late history load clobber it).
  const canSubmit =
    !historyPending && !busy && (!!input.trim() || attachments.length > 0);

  // Single submit (Odeslat). With staged attachments → extract invoices for
  // review (the typed message is shown as a caption; the assistant's summary is
  // the reply to it). Otherwise → a normal chat turn.
  const submit = async () => {
    if (busy) return;
    const text = input.trim();
    const files = attachments.map((a) => a.file);
    if (!text && files.length === 0) return;

    // Any interaction means the thread is "live"; a late-arriving history load
    // must not overwrite it (belt-and-suspenders alongside the canSubmit gate).
    seeded.current = true;
    const prevTurns = turns; // snapshot for rollback on failure
    setBusy(true);

    const userContent = [
      text,
      files.length > 0 ? `📎 ${files.map((f) => f.name).join(", ")}` : "",
    ]
      .filter(Boolean)
      .join("\n");
    const optimistic: ChatTurn[] = [
      ...turns,
      { role: "user", content: userContent },
    ];
    setTurns(optimistic);

    try {
      if (files.length > 0) {
        const fd = new FormData();
        files.forEach((f) => fd.append("files", f));
        const res = await apiFetch("/api/admin/ai/extract-invoices", {
          method: "POST",
          body: fd,
        });
        const body = await res.json();
        if (!res.ok) throw new Error(body?.error || "Chyba čtení faktur");
        // The server iterates parts serially and pushes one result per file in
        // submission order (truncating only if budget runs out mid-batch), so
        // files[i] is reliably this result's File — more robust than matching by
        // name, which would collide on duplicate filenames.
        const reviews: ReviewInvoice[] = (
          body.data.invoices as Array<{
            file_name: string;
            fields?: ExtractedInvoice;
            error?: string;
          }>
        )
          .map((r, i): ReviewInvoice | null => {
            const f = r.fields;
            return f
              ? {
                  uid: nextUid(),
                  supplier_name: f.supplier_name ?? "",
                  invoice_number: f.invoice_number ?? "",
                  amount: f.amount != null ? String(f.amount) : "",
                  currency: f.currency ?? "",
                  vat_rate: f.vat_rate != null ? String(f.vat_rate) : "",
                  issue_date: f.issue_date ?? "",
                  due_date: f.due_date ?? "",
                  description: f.description ?? "",
                  file_name: r.file_name,
                  file: files[i],
                }
              : null;
          })
          .filter((x): x is ReviewInvoice => x !== null);
        setReview((prev) => [...prev, ...reviews]);
        const note = summaryNote(reviews);
        setTurns((t) => [...t, { role: "assistant", content: note }]);
        setInput("");
        setAttachments([]);
        persist([
          { role: "user", content: userContent },
          { role: "assistant", content: note },
        ]);
        qc.invalidateQueries({ queryKey: ["ai", "usage"] });
      } else {
        // Send only recent context, and ensure it starts on a user turn (the
        // Anthropic API requires the first message to be from the user).
        let ctx = optimistic.slice(-MODEL_CONTEXT);
        while (ctx.length && ctx[0].role !== "user") ctx = ctx.slice(1);
        const res = await apiFetch("/api/admin/ai/chat", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ messages: ctx }),
        });
        const body = await res.json();
        if (!res.ok) throw new Error(body?.error || "Chyba AI");
        const reply: string = body.data.reply;
        setTurns((t) => [...t, { role: "assistant", content: reply }]);
        setInput("");
        persist([
          { role: "user", content: userContent },
          { role: "assistant", content: reply },
        ]);
        qc.invalidateQueries({ queryKey: ["ai", "usage"] });
      }
    } catch (e) {
      // Roll back the optimistic user turn and KEEP input + attachments so the
      // user can retry without losing their typed text or staged PDFs.
      setTurns(prevTurns);
      const fallback = files.length > 0 ? "Chyba čtení faktur" : "Chyba AI";
      alert.error(e instanceof Error ? e.message : fallback);
    } finally {
      setBusy(false);
    }
  };

  const saveInvoice = async (inv: ReviewInvoice) => {
    if (busy) return;
    setBusy(true);
    try {
      const fd = new FormData();
      fd.append("files", inv.file, inv.file_name);
      fd.append(
        "invoices",
        JSON.stringify([
          {
            supplier_name: inv.supplier_name,
            invoice_number: inv.invoice_number || undefined,
            description: inv.description || undefined,
            amount: inv.amount === "" ? undefined : Number(inv.amount),
            currency: inv.currency,
            vat_rate: inv.vat_rate === "" ? undefined : Number(inv.vat_rate),
            issue_date: inv.issue_date || undefined,
            due_date: inv.due_date || undefined,
          },
        ]),
      );
      const res = await apiFetch("/api/admin/received-invoices", {
        method: "POST",
        body: fd,
      });
      const body = await res.json();
      if (!res.ok) throw new Error(body?.error || "Uložení selhalo");
      alert.success("Faktura uložena");
      setReview((r) => r.filter((x) => x.uid !== inv.uid));
      qc.invalidateQueries({ queryKey: ["received-invoices"] });
    } catch (e) {
      alert.error(e instanceof Error ? e.message : "Uložení selhalo");
    } finally {
      setBusy(false);
    }
  };

  const clearHistory = async () => {
    if (busy) return;
    setBusy(true);
    try {
      const res = await apiFetch("/api/admin/ai/history", { method: "DELETE" });
      if (!res.ok) throw new Error("Nepodařilo se vymazat historii");
      setTurns([]);
      setReview([]);
    } catch (e) {
      alert.error(e instanceof Error ? e.message : "Chyba");
    } finally {
      setBusy(false);
    }
  };

  const patch = (uid: string, field: EditableField, value: string) =>
    setReview((r) =>
      r.map((inv) => (inv.uid === uid ? { ...inv, [field]: value } : inv)),
    );

  const fieldCols = { xs: "1fr", sm: "1fr 1fr" };

  return (
    <Card sx={{ mb: 3 }}>
      {/* Header */}
      <Box
        sx={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
          mb: 1.5,
        }}
      >
        <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
          <Box
            sx={{
              px: 0.75,
              py: 0.25,
              borderRadius: 1,
              fontSize: 11,
              fontWeight: 700,
              letterSpacing: 0.5,
              color: "common.white",
              bgcolor: "primary.main",
            }}
          >
            AI
          </Box>
          <Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
            Asistent
          </Typography>
        </Box>
        <Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
          {usage && (
            <Typography variant="caption" color="text.secondary">
              Utraceno: ${usage.month_spend_usd.toFixed(2)} / $
              {usage.budget_usd.toFixed(2)}
            </Typography>
          )}
          {turns.length > 0 && (
            <Button
              variant="text"
              color="inherit"
              size="small"
              onClick={clearHistory}
              disabled={busy}
              sx={{ minWidth: 0, color: "text.secondary" }}
            >
              Vymazat
            </Button>
          )}
        </Box>
      </Box>

      {/* Thread */}
      <Box
        ref={threadRef}
        sx={{
          display: "flex",
          flexDirection: "column",
          gap: 1,
          minHeight: 120,
          maxHeight: 360,
          overflowY: "auto",
          mb: 1.5,
          p: 1,
          borderRadius: 2,
          bgcolor: "action.hover",
        }}
      >
        {turns.length === 0 && !busy && (
          <Box
            sx={{
              flex: 1,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              textAlign: "center",
              color: "text.secondary",
              px: 2,
            }}
          >
            <Typography variant="body2" sx={{ color: "inherit" }}>
              {historyPending
                ? "Načítám…"
                : "Zeptejte se, nebo přiložte fakturu (PDF) k automatickému importu."}
            </Typography>
          </Box>
        )}

        {turns.map((t, i) => (
          <Box
            key={i}
            sx={{
              alignSelf: t.role === "user" ? "flex-end" : "flex-start",
              maxWidth: "85%",
              px: 1.5,
              py: 1,
              borderRadius: 2,
              boxShadow: 1,
              bgcolor: t.role === "user" ? "primary.main" : "background.paper",
            }}
          >
            {/* Color MUST sit on the Typography: GlobalStyles pins `p` to
                text.secondary, which beats a color merely inherited from the
                Box. An sx class on the element wins over that element rule. */}
            <Typography
              variant="body2"
              sx={{
                whiteSpace: "pre-wrap",
                color: t.role === "user" ? "common.white" : "text.primary",
              }}
            >
              {t.content}
            </Typography>
          </Box>
        ))}

        {busy && (
          <Box
            sx={{
              alignSelf: "flex-start",
              display: "flex",
              alignItems: "center",
              gap: 1,
              px: 1.5,
              py: 1,
              color: "text.secondary",
            }}
          >
            <CircularProgress size={14} />
            <Typography variant="caption">Pracuji…</Typography>
          </Box>
        )}
      </Box>

      {/* Pending invoices to confirm — rendered OUTSIDE the scrollable thread so
          the Uložit button is always reachable (the page scrolls if there are
          many), and editing a field never yanks the chat scroll. */}
      {review.length > 0 && (
        <Box sx={{ mb: 1.5 }}>
          <Typography
            variant="subtitle2"
            sx={{ fontWeight: 600, color: "text.secondary", mb: 1 }}
          >
            Faktury k potvrzení ({review.length})
          </Typography>
          <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
            {review.map((inv) => (
              <Card key={inv.uid} variant="outlined" sx={{ p: 1.5 }}>
                <Box
                  sx={{
                    display: "flex",
                    alignItems: "center",
                    gap: 1,
                    mb: 1,
                  }}
                >
                  <Chip label="Faktura" size="small" color="primary" />
                  <Typography
                    variant="caption"
                    color="text.secondary"
                    noWrap
                    title={inv.file_name}
                  >
                    {inv.file_name}
                  </Typography>
                </Box>
                <Box
                  sx={{
                    display: "grid",
                    gridTemplateColumns: fieldCols,
                    gap: 1,
                  }}
                >
                  <TextField
                    label="Dodavatel"
                    value={inv.supplier_name}
                    onChange={(e) =>
                      patch(inv.uid, "supplier_name", e.target.value)
                    }
                  />
                  <TextField
                    label="Číslo faktury"
                    value={inv.invoice_number}
                    onChange={(e) =>
                      patch(inv.uid, "invoice_number", e.target.value)
                    }
                  />
                  <TextField
                    label="Částka"
                    value={inv.amount}
                    onChange={(e) => patch(inv.uid, "amount", e.target.value)}
                  />
                  <TextField
                    label="Měna"
                    value={inv.currency}
                    onChange={(e) => patch(inv.uid, "currency", e.target.value)}
                  />
                  <TextField
                    label="Sazba DPH %"
                    value={inv.vat_rate}
                    onChange={(e) => patch(inv.uid, "vat_rate", e.target.value)}
                  />
                  <TextField
                    label="Datum vystavení"
                    value={inv.issue_date}
                    onChange={(e) =>
                      patch(inv.uid, "issue_date", e.target.value)
                    }
                  />
                  <TextField
                    label="Datum splatnosti"
                    value={inv.due_date}
                    onChange={(e) => patch(inv.uid, "due_date", e.target.value)}
                  />
                  <TextField
                    label="Popis"
                    value={inv.description}
                    onChange={(e) =>
                      patch(inv.uid, "description", e.target.value)
                    }
                  />
                </Box>
                <Box sx={{ display: "flex", gap: 1, mt: 1.5 }}>
                  <Button onClick={() => saveInvoice(inv)} disabled={busy}>
                    Uložit
                  </Button>
                  <Button
                    variant="outlined"
                    color="inherit"
                    onClick={() =>
                      setReview((r) => r.filter((x) => x.uid !== inv.uid))
                    }
                    disabled={busy}
                  >
                    Zahodit
                  </Button>
                </Box>
              </Card>
            ))}
          </Box>
        </Box>
      )}

      {/* Staged attachments */}
      {attachments.length > 0 && (
        <Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5, mb: 1 }}>
          {attachments.map((s) => (
            <Chip
              key={s.id}
              label={s.file.name}
              size="small"
              variant="outlined"
              onDelete={busy ? undefined : () => removeAttachment(s.id)}
            />
          ))}
        </Box>
      )}

      {/* Composer */}
      <Box sx={{ display: "flex", gap: 1, alignItems: "center" }}>
        <input
          ref={fileRef}
          type="file"
          accept="application/pdf,image/*"
          multiple
          hidden
          onChange={(e) => onFiles(e.target.files)}
        />
        <Button
          variant="outlined"
          color="inherit"
          onClick={() => fileRef.current?.click()}
          disabled={busy}
        >
          Přiložit
        </Button>
        <TextField
          fullWidth
          placeholder="Napište zprávu…"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === "Enter" && !e.shiftKey) {
              e.preventDefault();
              submit();
            }
          }}
        />
        <Button onClick={submit} disabled={!canSubmit}>
          Odeslat
        </Button>
      </Box>
    </Card>
  );
}
