import { useState } from "react";
import { Link, useParams, useSearchParams } from "react-router-dom";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Download, PackageOpen, Play, RefreshCw } from "lucide-react";
import { apiFetchBlob, saveBlob } from "../api/client";
import {
  explainObjectValidation,
  getObject,
  getObjectChain,
  getObjectProjection,
  getObjectValidation,
  rawObjectUrl,
} from "../api/service";
import type { ValidationIssue } from "../api/schemas";
import { CopyableValue } from "../components/CopyableValue";
import { DataTable, type Column } from "../components/DataTable";
import { PageHeader } from "../components/PageHeader";
import { Panel } from "../components/Panel";
import { CertificateView, ProjectionView } from "../components/ProjectionView";
import { StatusPill } from "../components/StatusPill";
import { EmptyBlock, ErrorBlock, LoadingBlock, Notice } from "../components/StateBlock";
import { TabPanel, Tabs } from "../components/Tabs";
import { WorkflowStatus } from "../components/WorkflowStatus";
import { formatInt, objectTypeLabel } from "../lib/format";
import { eeCertificateProjection } from "../lib/projection";
import { withRunParam } from "../lib/run";
import { useExportJob } from "../lib/useExportJob";
import { useRun } from "../lib/useRun";

function filenameFor(uri: string | undefined, sha256: string | undefined, type: string): string {
  const last = uri?.split("/").filter(Boolean).pop();
  if (last && last.length <= 128) return last;
  const ext = type === "mft" ? ".mft" : type === "crl" ? ".crl" : type === "roa" ? ".roa" : type === "asa" ? ".asa" : ".cer";
  return `${(sha256 ?? "object").slice(0, 16)}${ext}`;
}

function IssueList({ issues }: { issues: ValidationIssue[] }) {
  if (issues.length === 0) return <p className="text-muted">No issues recorded.</p>;
  return (
    <ul style={{ margin: 0, paddingLeft: 18, display: "flex", flexDirection: "column", gap: 6 }}>
      {issues.map((issue, i) => (
        <li key={i}>
          <StatusPill status={issue.severity} />
          {issue.reasonCode ? <code style={{ marginLeft: 8 }}>{issue.reasonCode}</code> : null}
          <span style={{ marginLeft: 8 }}>{issue.reasonText ?? "—"}</span>
          {issue.rfcRefs?.length ? (
            <span className="text-faint text-small" style={{ marginLeft: 8 }}>
              [{issue.rfcRefs.join(", ")}]
            </span>
          ) : null}
        </li>
      ))}
    </ul>
  );
}

function ValidationTab({ runId, objectInstanceId }: { runId: string; objectInstanceId: string }) {
  const [forceRefresh, setForceRefresh] = useState(false);
  const validationQuery = useQuery({
    queryKey: ["object-validation", runId, objectInstanceId],
    queryFn: () => getObjectValidation(runId, objectInstanceId),
    staleTime: 5 * 60_000,
  });
  const explainMutation = useMutation({
    mutationFn: () => explainObjectValidation(runId, objectInstanceId, { forceRefresh }),
  });

  if (validationQuery.isPending) return <LoadingBlock label="Loading validation summary…" />;
  if (validationQuery.isError) {
    return (
      <ErrorBlock
        error={validationQuery.error}
        onRetry={() => validationQuery.refetch()}
        title="Failed to load validation summary"
      />
    );
  }

  const validation = validationQuery.data;
  const explain = explainMutation.data;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16, padding: 16 }}>
      <dl className="meta-grid">
        <dt>Final status</dt>
        <dd><StatusPill status={validation.finalStatus} /></dd>
        <dt>Audit result</dt>
        <dd><StatusPill status={validation.auditResult} /></dd>
        <dt>Detail</dt>
        <dd>{validation.detailSummary ?? "—"}</dd>
      </dl>

      <section>
        <h3 className="text-small" style={{ marginBottom: 8 }}>File validation (parsevalidate)</h3>
        <p style={{ marginBottom: 8 }}>
          <StatusPill status={validation.parsevalidate?.status} />
        </p>
        <IssueList issues={validation.parsevalidate?.issues ?? []} />
      </section>

      <section>
        <h3 className="text-small" style={{ marginBottom: 8 }}>Chain validation (chainvalidate)</h3>
        <p style={{ marginBottom: 8 }}>
          <StatusPill status={validation.chainvalidate?.status} />
        </p>
        <IssueList issues={validation.chainvalidate?.issues ?? []} />
      </section>

      <section>
        <h3 className="text-small" style={{ marginBottom: 8 }}>Explain validation</h3>
        <div style={{ display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap", marginBottom: 8 }}>
          <button
            type="button"
            className="btn"
            onClick={() => explainMutation.mutate()}
            disabled={explainMutation.isPending}
          >
            {explainMutation.isPending ? (
              <span className="spinner small" aria-hidden="true" />
            ) : (
              <Play size={13} aria-hidden="true" />
            )}
            {explain ? "Run again" : "Run explain"}
          </button>
          <label className="filter-check" style={{ paddingBottom: 0 }}>
            <input
              type="checkbox"
              checked={forceRefresh}
              onChange={(e) => setForceRefresh(e.target.checked)}
            />
            Force refresh (bypass cached explain)
          </label>
        </div>
        {explainMutation.isError ? (
          <Notice kind="error">Explain failed: {explainMutation.error.message}</Notice>
        ) : null}
        {explain ? (
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <Notice kind="info">
              Explain mode: <strong>{explain.explainMode ?? "audit projection"}</strong> —{" "}
              {explain.authoritative
                ? "authoritative revalidation."
                : "not a full revalidation; derived from the run audit trail."}
            </Notice>
            <dl className="meta-grid">
              <dt>Final status</dt>
              <dd><StatusPill status={explain.finalStatus} /></dd>
              <dt>Parse stage</dt>
              <dd><StatusPill status={explain.parsevalidate?.status} /></dd>
              <dt>Chain stage</dt>
              <dd>
                <StatusPill status={explain.chainvalidate?.status} />{" "}
                <span className="text-muted text-small">
                  {formatInt(explain.chainvalidate?.edgesCount)} chain edges
                </span>
              </dd>
              {explain.chainvalidate?.note ? (
                <>
                  <dt>Note</dt>
                  <dd>{explain.chainvalidate.note}</dd>
                </>
              ) : null}
            </dl>
          </div>
        ) : null}
      </section>
    </div>
  );
}

function ChainTab({ runId, objectInstanceId }: { runId: string; objectInstanceId: string }) {
  const chainQuery = useQuery({
    queryKey: ["object-chain", runId, objectInstanceId],
    queryFn: () => getObjectChain(runId, objectInstanceId),
    staleTime: 5 * 60_000,
  });

  if (chainQuery.isPending) return <LoadingBlock label="Loading chain edges…" />;
  if (chainQuery.isError) {
    return <ErrorBlock error={chainQuery.error} onRetry={() => chainQuery.refetch()} title="Failed to load chain" />;
  }
  const edges = chainQuery.data;
  if (edges.length === 0) {
    return <EmptyBlock title="No chain edges" hint="No issuer/manifest relationships were recorded for this object." />;
  }

  const columns: Column<(typeof edges)[number]>[] = [
    { key: "relation", header: "Relation", render: (edge) => <span className="chip">{edge.relation}</span> },
    {
      key: "from",
      header: "From URI",
      render: (edge) => <CopyableValue value={edge.fromUri} max={48} label="from URI" />,
    },
    {
      key: "to",
      header: "To URI",
      render: (edge) => <CopyableValue value={edge.toUri} max={48} label="to URI" />,
    },
    { key: "status", header: "Status", render: (edge) => <StatusPill status={edge.status} /> },
  ];

  return <DataTable columns={columns} rows={edges} rowKey={(e) => `${e.relation}:${e.fromUri}:${e.toUri}`} caption="Certificate chain edges" />;
}

export default function ObjectDetailPage() {
  const { objectInstanceId = "" } = useParams();
  const { runId } = useRun();
  const [searchParams, setSearchParams] = useSearchParams();
  const tab = searchParams.get("tab") ?? "parsed";

  const objectQuery = useQuery({
    queryKey: ["object", runId, objectInstanceId],
    queryFn: () => getObject(runId, objectInstanceId),
  });

  // Always fetch the parsed projection: the Parsed tab needs it, and the
  // conditional "EE Parsed" tab derives from it (CMS-signed objects only).
  const projectionQuery = useQuery({
    queryKey: ["object-parsed", runId, objectInstanceId],
    queryFn: () => getObjectProjection(runId, objectInstanceId),
    staleTime: 5 * 60_000,
  });

  const rawMutation = useMutation({
    mutationFn: async () => {
      const blob = await apiFetchBlob(rawObjectUrl(runId, objectInstanceId));
      const object = objectQuery.data;
      saveBlob(blob, filenameFor(object?.uri, object?.sha256, object?.objectType ?? ""));
    },
  });

  const {
    startMutation: exportMutation,
    jobQuery: exportJobQuery,
    running: exportRunning,
  } = useExportJob(runId, { scope: "object_set", objectInstanceIds: [objectInstanceId] });

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

  const object = objectQuery.data;
  const eeProjection = eeCertificateProjection(projectionQuery.data ?? null);

  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("/objects", runId)}>Objects</Link>
        <span aria-hidden="true">/</span>
        <span>{objectTypeLabel(object.objectType)}</span>
      </nav>

      <PageHeader
        title={
          <span style={{ display: "inline-flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
            {objectTypeLabel(object.objectType)} object
            <StatusPill status={object.rejected ? "rejected" : object.result} />
            <StatusPill status={object.sourceSection} />
          </span>
        }
        subtitle={<CopyableValue value={object.uri} max={110} label="object URI" />}
        actions={
          <>
            <button
              type="button"
              className="btn"
              onClick={() => rawMutation.mutate()}
              disabled={rawMutation.isPending}
            >
              {rawMutation.isPending ? <span className="spinner small" aria-hidden="true" /> : <Download size={13} aria-hidden="true" />}
              Download raw
            </button>
            <button
              type="button"
              className="btn"
              onClick={() => exportMutation.mutate()}
              disabled={exportRunning}
            >
              {exportMutation.isPending ? <RefreshCw size={13} aria-hidden="true" /> : <PackageOpen size={13} aria-hidden="true" />}
              Export object set
            </button>
          </>
        }
      />

      {rawMutation.isError ? (
        <Notice kind="error">
          Raw download failed: {rawMutation.error.message}. The query service needs
          --repo-bytes-db to serve raw bytes.
        </Notice>
      ) : null}
      {exportMutation.isError ? (
        <Notice kind="error">Export failed to start: {exportMutation.error.message}</Notice>
      ) : null}
      {exportJobQuery.data ? <WorkflowStatus job={exportJobQuery.data} runId={runId} /> : null}
      {exportJobQuery.data?.status === "complete" ? (
        <Notice kind="info">
          Export complete — {formatInt(exportJobQuery.data.objectCount)} objects. Find it any
          time on the <Link to={withRunParam("/exports", runId)}>Exports page</Link>.
        </Notice>
      ) : null}

      <Panel className="object-header-card">
        <dl className="meta-grid">
          <dt>Object type</dt>
          <dd><span className="chip">{objectTypeLabel(object.objectType)}</span></dd>
          <dt>URI</dt>
          <dd><CopyableValue value={object.uri} max={110} label="object URI" /></dd>
          <dt>SHA-256</dt>
          <dd><CopyableValue value={object.sha256} max={72} label="object sha256" /></dd>
          <dt>Instance ID</dt>
          <dd><CopyableValue value={object.objectInstanceId} max={56} label="object instance id" /></dd>
          <dt>Result</dt>
          <dd><StatusPill status={object.rejected ? "rejected" : object.result} /></dd>
          <dt>Reject reason</dt>
          <dd>{object.rejectReason ?? <span className="text-faint">—</span>}</dd>
          <dt>Detail</dt>
          <dd>{object.detailSummary ?? <span className="text-faint">—</span>}</dd>
          <dt>Repository</dt>
          <dd>
            <Link className="mono" to={withRunParam(`/repositories/${encodeURIComponent(object.repoId)}`, runId)}>
              {object.repoId}
            </Link>
          </dd>
          <dt>Publication point</dt>
          <dd>
            <Link className="mono" to={withRunParam(`/publication-points/${encodeURIComponent(object.ppId)}`, runId)}>
              {object.ppId}
            </Link>
          </dd>
        </dl>
      </Panel>

      <Panel flush>
        <Tabs
          tabs={[
            { id: "parsed", label: "Parsed" },
            ...(eeProjection ? [{ id: "ee", label: "EE Parsed" }] : []),
            { id: "validation", label: "Validation" },
            { id: "chain", label: "Chain" },
          ]}
          active={tab}
          onChange={setTab}
          ariaLabel="Object detail sections"
        />
        <TabPanel id="parsed" active={tab}>
          <div style={{ padding: 16 }}>
            {projectionQuery.isPending ? (
              <LoadingBlock label="Loading parsed projection…" />
            ) : projectionQuery.isError ? (
              <ErrorBlock
                error={projectionQuery.error}
                onRetry={() => projectionQuery.refetch()}
                title="Failed to load parsed projection"
              />
            ) : (
              <ProjectionView
                record={projectionQuery.data ?? null}
                runId={runId}
                objectInstanceId={objectInstanceId}
              />
            )}
          </div>
        </TabPanel>
        {eeProjection ? (
          <TabPanel id="ee" active={tab}>
            <div style={{ padding: 16 }}>
              <CertificateView projection={eeProjection} />
            </div>
          </TabPanel>
        ) : tab === "ee" ? (
          <div style={{ padding: 16 }}>
            <Notice kind="info">This object has no embedded EE certificate in its CMS wrapper.</Notice>
          </div>
        ) : null}
        <TabPanel id="validation" active={tab}>
          <ValidationTab runId={runId} objectInstanceId={objectInstanceId} />
        </TabPanel>
        <TabPanel id="chain" active={tab}>
          <ChainTab runId={runId} objectInstanceId={objectInstanceId} />
        </TabPanel>
      </Panel>
    </div>
  );
}
