"use client";

import { useMemo, useState } from "react";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
  Modal,
  ModalTitle,
  ModalDescription,
  ModalClose,
} from "@/components/modal";
import type {
  AdminErrorOverview,
  AdminErrorRow,
  AdminErrorsResponse,
} from "@ttsa/shared";
import type { ColumnDef, PaginationState } from "@tanstack/react-table";
import { PageHeader, StatCard } from "@/components/admin/shell";
import { DataTable, fmtDate, truncate } from "@/components/admin/data-table";
import { BarChartCard, HBarChartCard } from "@/components/admin/charts";

/* A small fixed palette so each source gets a stable color in the stacked chart. */
const PALETTE = [
  "var(--color-accent)",
  "#f59e0b",
  "#3b82f6",
  "#10b981",
  "#a855f7",
  "#ef4444",
  "#14b8a6",
  "#eab308",
  "#ec4899",
  "#6366f1",
];

const SEV_COLOR: Record<string, string> = {
  fatal: "text-accent",
  error: "text-accent",
  warn: "text-amber-500",
};

async function fetchOverview(): Promise<AdminErrorOverview> {
  const res = await fetch("/api/admin/errors/overview");
  if (!res.ok) throw new Error("failed");
  return res.json();
}

async function fetchErrors(params: {
  page: number;
  pageSize: number;
  source?: string;
  severity?: string;
  search?: string;
}): Promise<AdminErrorsResponse> {
  const sp = new URLSearchParams({
    page: String(params.page),
    pageSize: String(params.pageSize),
  });
  if (params.source) sp.set("source", params.source);
  if (params.severity) sp.set("severity", params.severity);
  if (params.search) sp.set("search", params.search);
  const res = await fetch(`/api/admin/errors?${sp}`);
  if (!res.ok) throw new Error("failed");
  return res.json();
}

export default function AdminErrorsPage() {
  const { data: ov } = useQuery({
    queryKey: ["admin", "errors", "overview"],
    queryFn: fetchOverview,
    refetchInterval: 30_000,
  });

  return (
    <div>
      <PageHeader
        title="Errors"
        subtitle="Application errors across services — trends, failing models, and a searchable log."
      />

      {!ov ? (
        <p className="text-sm text-ink-3">Loading…</p>
      ) : (
        <div className="flex flex-col gap-5">
          <div className="grid grid-cols-2 gap-3 lg:grid-cols-4">
            <StatCard label="Last 24h" value={ov.last24h.toLocaleString()} />
            <StatCard label="Last 7d" value={ov.last7d.toLocaleString()} />
            <StatCard
              label="Distinct sources"
              value={ov.distinctSources.toLocaleString()}
            />
            <StatCard
              label="Top failing model"
              value={
                ov.topFailingModel
                  ? `${truncate(ov.topFailingModel.model, 16)}`
                  : "—"
              }
              hint={
                ov.topFailingModel
                  ? `${ov.topFailingModel.count} in 7d`
                  : undefined
              }
            />
          </div>

          {/* Errors per day, stacked by source */}
          <BarChartCard
            title="Errors per day (by source, 30d)"
            data={ov.errorsByDay.map((d) => ({ date: d.date, ...d.bySource }))}
            xKey="date"
            stacked
            height={240}
            series={ov.sources.map((s, i) => ({
              key: s,
              label: s,
              color: PALETTE[i % PALETTE.length],
            }))}
          />

          <div className="grid gap-4 lg:grid-cols-2">
            {ov.byModel.length > 0 ? (
              <HBarChartCard
                title="Failures by model (7d)"
                data={ov.byModel.map((m) => ({
                  model: truncate(m.model, 18),
                  count: m.count,
                }))}
                labelKey="model"
                valueKey="count"
              />
            ) : (
              <EmptyCard title="Failures by model (7d)" />
            )}

            {ov.byProvider.length > 0 ? (
              <HBarChartCard
                title="Failures by provider (7d)"
                data={ov.byProvider.map((p) => ({
                  provider: truncate(p.provider, 18),
                  count: p.count,
                }))}
                labelKey="provider"
                valueKey="count"
                color="#3b82f6"
              />
            ) : (
              <EmptyCard title="Failures by provider (7d)" />
            )}
          </div>

          {/* Breakdown by source */}
          <div className="card p-4">
            <p className="tag mb-3">Errors by source (7d)</p>
            <ul className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
              {ov.bySource.length === 0 && (
                <li className="text-sm text-ink-3">No errors. Nice.</li>
              )}
              {ov.bySource.map((s) => (
                <li
                  key={s.source}
                  className="flex items-center justify-between rounded-md bg-fill/60 px-3 py-1.5 text-sm"
                >
                  <span className="font-mono text-xs">{s.source}</span>
                  <span className="nums font-medium">{s.count}</span>
                </li>
              ))}
            </ul>
          </div>

          <ErrorLog sources={ov.sources} />
        </div>
      )}
    </div>
  );
}

function EmptyCard({ title }: { title: string }) {
  return (
    <div className="card flex flex-col p-4">
      <p className="tag mb-3">{title}</p>
      <p className="flex-1 text-sm text-ink-3">No data in the window.</p>
    </div>
  );
}

function ErrorLog({ sources }: { sources: string[] }) {
  const [source, setSource] = useState("");
  const [severity, setSeverity] = useState("");
  const [search, setSearch] = useState("");
  const [selected, setSelected] = useState<AdminErrorRow | null>(null);
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: 0,
    pageSize: 50,
  });

  const { data, isLoading, isFetching } = useQuery({
    queryKey: [
      "admin",
      "errors",
      "list",
      pagination.pageIndex,
      pagination.pageSize,
      source,
      severity,
      search,
    ],
    queryFn: () =>
      fetchErrors({
        page: pagination.pageIndex,
        pageSize: pagination.pageSize,
        source: source || undefined,
        severity: severity || undefined,
        search: search || undefined,
      }),
    placeholderData: keepPreviousData,
  });

  const pageCount = data
    ? Math.max(1, Math.ceil(data.total / pagination.pageSize))
    : 1;

  const resetPage = () => setPagination((p) => ({ ...p, pageIndex: 0 }));

  const columns = useMemo<ColumnDef<AdminErrorRow, unknown>[]>(
    () => [
      {
        accessorKey: "createdAt",
        header: "When",
        enableSorting: false,
        cell: (c) => (
          <span className="tag whitespace-nowrap">
            {fmtDate(c.row.original.createdAt)}
          </span>
        ),
      },
      {
        accessorKey: "severity",
        header: "Sev",
        enableSorting: false,
        cell: (c) => (
          <span
            className={`text-xs font-medium uppercase ${
              SEV_COLOR[c.row.original.severity] ?? "text-ink-3"
            }`}
          >
            {c.row.original.severity}
          </span>
        ),
      },
      {
        accessorKey: "source",
        header: "Source",
        enableSorting: false,
        cell: (c) => (
          <span className="font-mono text-xs">{c.row.original.source}</span>
        ),
      },
      {
        id: "target",
        header: "Model / Provider",
        enableSorting: false,
        cell: (c) => {
          const r = c.row.original;
          if (!r.model && !r.provider)
            return <span className="text-ink-4">—</span>;
          return (
            <span className="text-ink-2">
              {r.model ?? r.provider}
              {r.status ? (
                <span className="ml-1 text-ink-4">({r.status})</span>
              ) : null}
            </span>
          );
        },
      },
      {
        accessorKey: "message",
        header: "Message",
        enableSorting: false,
        cell: (c) => (
          <span className="text-ink-3">
            {truncate(c.row.original.message, 70)}
          </span>
        ),
      },
    ],
    [],
  );

  return (
    <div className="flex flex-col gap-3">
      <div className="flex flex-wrap items-center gap-2">
        <input
          value={search}
          onChange={(e) => {
            setSearch(e.target.value);
            resetPage();
          }}
          placeholder="Search message…"
          className="w-56 rounded-md border border-line bg-transparent px-3 py-1.5 text-sm outline-none focus:border-ink-3"
        />
        <select
          value={source}
          onChange={(e) => {
            setSource(e.target.value);
            resetPage();
          }}
          className="rounded-md border border-line bg-transparent px-2 py-1.5 text-sm text-ink-2 outline-none"
        >
          <option value="">All sources</option>
          {sources.map((s) => (
            <option key={s} value={s}>
              {s}
            </option>
          ))}
        </select>
        <select
          value={severity}
          onChange={(e) => {
            setSeverity(e.target.value);
            resetPage();
          }}
          className="rounded-md border border-line bg-transparent px-2 py-1.5 text-sm text-ink-2 outline-none"
        >
          <option value="">All severities</option>
          <option value="warn">warn</option>
          <option value="error">error</option>
          <option value="fatal">fatal</option>
        </select>
        {data && (
          <span className="tag ml-auto">
            {data.total.toLocaleString()} events
          </span>
        )}
      </div>

      <DataTable
        columns={columns}
        data={data?.rows ?? []}
        manualPagination
        pageCount={pageCount}
        pagination={pagination}
        onPaginationChange={setPagination}
        loading={isLoading || isFetching}
        emptyMessage="No errors match."
        onRowClick={setSelected}
        pageSizeOptions={[50, 100]}
      />

      <ErrorDetailDialog row={selected} onClose={() => setSelected(null)} />
    </div>
  );
}

function ErrorDetailDialog({
  row,
  onClose,
}: {
  row: AdminErrorRow | null;
  onClose: () => void;
}) {
  return (
    <Modal
      open={!!row}
      onClose={onClose}
      size="lg"
      className="max-h-[85vh] overflow-y-auto"
    >
      {row && (
        <div className="flex flex-col gap-4">
          <div>
            <ModalTitle className="font-mono text-sm font-normal text-ink-3">
              {row.source}
              {row.route ? ` · ${row.method ?? ""} ${row.route}` : ""}
            </ModalTitle>
            <ModalDescription className="mt-1 text-base font-medium break-words text-ink">
              {row.message}
            </ModalDescription>
            <p className="tag mt-1">{fmtDate(row.createdAt)}</p>
          </div>

          <div className="grid grid-cols-2 gap-2 text-sm sm:grid-cols-3">
            <Meta label="Severity" value={row.severity} />
            <Meta label="Provider" value={row.provider} />
            <Meta label="Model" value={row.model} />
            <Meta
              label="Status"
              value={row.status != null ? String(row.status) : null}
            />
            <Meta
              label="User"
              value={row.userId != null ? String(row.userId) : null}
            />
          </div>

          {row.detail && (
            <div>
              <p className="tag mb-1.5">Detail</p>
              <pre className="max-h-40 overflow-auto rounded-md bg-fill/60 p-3 font-mono text-xs whitespace-pre-wrap text-ink-2">
                {prettyJson(row.detail)}
              </pre>
            </div>
          )}

          {row.stack && (
            <div>
              <p className="tag mb-1.5">Stack</p>
              <pre className="max-h-72 overflow-auto rounded-md bg-fill/60 p-3 font-mono text-xs whitespace-pre-wrap text-ink-2">
                {row.stack}
              </pre>
            </div>
          )}

          <div className="flex justify-end">
            <ModalClose className="rounded-md bg-fill px-3 py-1.5 text-sm font-medium transition-colors hover:bg-line">
              Close
            </ModalClose>
          </div>
        </div>
      )}
    </Modal>
  );
}

function Meta({ label, value }: { label: string; value: string | null }) {
  return (
    <div>
      <p className="tag">{label}</p>
      <p className="text-ink-2">{value || "—"}</p>
    </div>
  );
}

function prettyJson(s: string): string {
  try {
    return JSON.stringify(JSON.parse(s), null, 2);
  } catch {
    return s;
  }
}
