import { useMemo } from "react";
import { Link, useNavigate, useParams, useSearchParams } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { PackageOpen, RefreshCw } from "lucide-react";
import {
  getRepo,
  getRepoStats,
  listRepoObjects,
  listRepoPublicationPoints,
} from "../api/service";
import type { PublicationPointRecord } from "../api/schemas";
import { CopyableValue } from "../components/CopyableValue";
import { CursorPagerControls } from "../components/CursorPagerControls";
import { DataTable, type Column } from "../components/DataTable";
import { KpiCard } from "../components/KpiCard";
import {
  objectFiltersFromParams,
  objectFiltersToParams,
  ObjectsTable,
  type ObjectFilterValues,
} from "../components/ObjectsTable";
import { PageHeader } from "../components/PageHeader";
import { Panel } from "../components/Panel";
import { StatusPill } from "../components/StatusPill";
import { TabPanel, Tabs } from "../components/Tabs";
import { WorkflowStatus } from "../components/WorkflowStatus";
import { ErrorBlock, LoadingBlock, Notice } from "../components/StateBlock";
import { useCursorPager } from "../lib/cursor";
import { formatDurationMs, formatInt, formatUtc, truncateMiddle } from "../lib/format";
import { withRunParam } from "../lib/run";
import { useExportJob } from "../lib/useExportJob";
import { useRun } from "../lib/useRun";

function RepoPpsTable({ runId, repoId }: { runId: string; repoId: string }) {
  const navigate = useNavigate();
  const pager = useCursorPager(`${runId}:${repoId}`);
  const ppsQuery = useQuery({
    queryKey: ["repo-pps", runId, repoId, pager.cursor],
    queryFn: () => listRepoPublicationPoints(runId, repoId, { limit: 50, cursor: pager.cursor }),
    placeholderData: (prev) => prev,
  });

  const columns: Column<PublicationPointRecord>[] = [
    {
      key: "manifest",
      header: "Manifest URI",
      render: (pp) => (
        <span className="mono text-small ellipsis" style={{ display: "block", maxWidth: 420 }} title={pp.manifestRsyncUri ?? undefined}>
          {pp.manifestRsyncUri ? truncateMiddle(pp.manifestRsyncUri, 72) : pp.ppId}
        </span>
      ),
    },
    {
      key: "source",
      header: "Sync source",
      render: (pp) => <span className="chip">{pp.repoSyncSource ?? pp.source ?? "—"}</span>,
    },
    {
      key: "state",
      header: "Terminal state",
      render: (pp) => <StatusPill status={pp.repoTerminalState} />,
    },
    { key: "objects", header: "Objects", numeric: true, render: (pp) => formatInt(pp.objects) },
    {
      key: "rejected",
      header: "Rejected",
      numeric: true,
      render: (pp) =>
        pp.rejectedObjects ? (
          <span style={{ color: "var(--red-700)", fontWeight: 600 }}>{formatInt(pp.rejectedObjects)}</span>
        ) : (
          "0"
        ),
    },
    {
      key: "duration",
      header: "Sync time",
      numeric: true,
      render: (pp) => formatDurationMs(pp.repoSyncDurationMs),
    },
  ];

  return (
    <>
      <DataTable
        columns={columns}
        rows={ppsQuery.data?.items ?? []}
        rowKey={(pp) => pp.ppId}
        loading={ppsQuery.isPending}
        error={ppsQuery.isError ? ppsQuery.error : undefined}
        onRetry={() => ppsQuery.refetch()}
        emptyTitle="No publication points"
        caption="Publication points in this repository"
        onRowClick={(pp) =>
          navigate(withRunParam(`/publication-points/${encodeURIComponent(pp.ppId)}`, runId))
        }
      />
      <CursorPagerControls
        page={pager.page}
        canPrev={pager.canPrev}
        hasNext={ppsQuery.data?.nextCursor != null}
        loading={ppsQuery.isFetching}
        onPrev={pager.goPrev}
        onNext={() => pager.goNext(ppsQuery.data?.nextCursor ?? null)}
        itemCount={ppsQuery.data?.items.length}
      />
    </>
  );
}

export default function RepositoryDetailPage() {
  const { repoId = "" } = useParams();
  const { runId, runQuery } = useRun();
  const [searchParams, setSearchParams] = useSearchParams();
  const tab = searchParams.get("tab") ?? "pps";
  // Object filters live in the URL so they survive reloads and stay shareable.
  const objectFilters = useMemo(() => objectFiltersFromParams(searchParams), [searchParams]);

  const setObjectFilters = (next: ObjectFilterValues) => {
    setSearchParams(objectFiltersToParams(next, searchParams), { preventScrollReset: true });
  };

  const repoQuery = useQuery({
    queryKey: ["repo", runId, repoId],
    queryFn: () => getRepo(runId, repoId),
  });
  const statsQuery = useQuery({
    queryKey: ["repo-stats", runId, repoId],
    queryFn: () => getRepoStats(runId, repoId),
  });
  const exportJob = useExportJob(runId, { scope: "repo", repoId });

  if (repoQuery.isError) {
    return (
      <div className="page">
        <PageHeader title="Repository" />
        <ErrorBlock error={repoQuery.error} onRetry={() => repoQuery.refetch()} title="Failed to load repository" />
      </div>
    );
  }
  if (repoQuery.isPending) {
    return (
      <div className="page">
        <PageHeader title="Repository" />
        <LoadingBlock />
      </div>
    );
  }

  const repo = repoQuery.data;
  const stats = statsQuery.data;

  const setTab = (next: string) => {
    const params = new URLSearchParams(searchParams);
    params.set("tab", next);
    setSearchParams(params, { preventScrollReset: true });
  };

  return (
    <div className="page">
      <nav className="breadcrumbs" aria-label="Breadcrumb">
        <Link to={withRunParam("/repositories", runId)}>Repositories</Link>
        <span aria-hidden="true">/</span>
        <span>{repo.host}</span>
      </nav>

      <PageHeader
        title={repo.host}
        subtitle={<CopyableValue value={repo.uri} max={96} label="repository URI" />}
        actions={
          <>
            <StatusPill status={runQuery.data?.indexStatus} label={`run ${runQuery.data?.runId ?? runId}`} />
            <button
              type="button"
              className="btn"
              onClick={() => exportJob.startMutation.mutate()}
              disabled={exportJob.running}
            >
              {exportJob.startMutation.isPending ? (
                <RefreshCw size={13} aria-hidden="true" />
              ) : (
                <PackageOpen size={13} aria-hidden="true" />
              )}
              Export
            </button>
          </>
        }
      />

      {exportJob.startMutation.isError ? (
        <Notice kind="error">Export failed to start: {exportJob.startMutation.error.message}</Notice>
      ) : null}
      {exportJob.jobQuery.data ? <WorkflowStatus job={exportJob.jobQuery.data} runId={runId} /> : null}

      <div className="kpi-grid">
        <KpiCard label="Publication points" value={formatInt(repo.publicationPoints)} />
        <KpiCard label="Objects" value={formatInt(repo.objects)} />
        <KpiCard label="Rejected" value={formatInt(repo.rejectedObjects)} tone="red" />
        <KpiCard label="Sync time" value={formatDurationMs(repo.syncDurationMsTotal)} />
      </div>

      <Panel title="Repository details">
        <dl className="meta-grid">
          <dt>Repository ID</dt>
          <dd><CopyableValue value={repo.repoId} max={48} label="repository id" /></dd>
          <dt>URI</dt>
          <dd><CopyableValue value={repo.uri} max={96} label="repository URI" /></dd>
          <dt>Host</dt>
          <dd>{repo.host}</dd>
          <dt>Transport</dt>
          <dd><span className="chip">{repo.transport ?? "unknown"}</span></dd>
          <dt>Sync phases</dt>
          <dd>
            <span className="chip-list">
              {Object.entries(stats?.phases ?? repo.phases ?? {}).map(([phase, count]) => (
                <span className="chip" key={phase}>{phase} {formatInt(count)}</span>
              ))}
              {Object.keys(stats?.phases ?? repo.phases ?? {}).length === 0 && <span className="text-faint">—</span>}
            </span>
          </dd>
          <dt>Terminal states</dt>
          <dd>
            <span className="chip-list">
              {Object.entries(stats?.terminalStates ?? repo.terminalStates ?? {}).map(([state, count]) => (
                <StatusPill key={state} status={state} label={`${state} ${formatInt(count)}`} />
              ))}
              {Object.keys(stats?.terminalStates ?? repo.terminalStates ?? {}).length === 0 && <span className="text-faint">—</span>}
            </span>
          </dd>
          <dt>Run validated</dt>
          <dd>{formatUtc(runQuery.data?.validationTime)}</dd>
        </dl>
      </Panel>

      <Panel flush>
        <Tabs
          tabs={[
            { id: "pps", label: "Publication points" },
            { id: "objects", label: "Objects" },
          ]}
          active={tab}
          onChange={setTab}
          ariaLabel="Repository sections"
        />
        <TabPanel id="pps" active={tab}>
          <RepoPpsTable runId={runId} repoId={repoId} />
        </TabPanel>
        <TabPanel id="objects" active={tab}>
          <ObjectsTable
            runId={runId}
            queryKeyScope={`repo:${repoId}`}
            fetcher={(filters) => listRepoObjects(runId, repoId, filters)}
            filters={objectFilters}
            onFiltersChange={setObjectFilters}
          />
        </TabPanel>
      </Panel>
    </div>
  );
}
