import { useEffect, useRef, useState } from "react";
import { ApiError, clinicianApi } from "../../api/client";
import type { CheckRun, Extraction, PatientDetail } from "../../api/types";
import type { CoverageReview } from "../../coverage";
import { plural } from "../../flow";
import type { Review } from "../../types";
import { Button, cn, Icon, type IconName, Notice, StatusPill, type Tone } from "../kit";
import { NewInformation } from "../patient/Overview";
import { RecordsPanel } from "../patient/Records";
import { errorText, Spinner } from "../ui";
import { FindingsView } from "./FindingsView";
import { ReviewStep } from "./ReviewStep";

type Step = "records" | "review" | "findings";
const readLook: Record<string, { label: string; tone: Tone; icon: IconName }> = {
  read: { label: "Read", tone: "green", icon: "checkbox-circle-fill" },
  failed: { label: "Not read", tone: "red", icon: "close-circle-fill" },
  held_back: { label: "Held back", tone: "amber", icon: "spam-2-fill" },
};

/** Records → Review → Findings (PAT-3 Check). Decisions save on the server with a revision (PAT-6). */
export function CheckTab({ patient, onChanged }: { patient: PatientDetail; onChanged: () => void }) {
  const [extraction, setExtraction] = useState<Extraction | null>(null);
  const [check, setCheck] = useState<CheckRun | null>(null);
  const [step, setStep] = useState<Step>("records");
  const [ready, setReady] = useState(false);
  const [busy, setBusy] = useState("");
  const [error, setError] = useState("");
  const [conflict, setConflict] = useState(false);
  const [review, setReview] = useState<Review>({});
  const [coverage, setCoverage] = useState<CoverageReview>({});
  const [acknowledged, setAcknowledged] = useState(false);
  const [confirmed, setConfirmed] = useState<string[]>([]);
  const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const saving = useRef<Promise<void>>(Promise.resolve());
  const revision = useRef(0);

  const adopt = (ex: Extraction) => {
    setExtraction(ex);
    setReview(ex.decisions);
    setCoverage(ex.coverageReview);
    setAcknowledged(ex.acknowledgedOriginals);
    setConfirmed(ex.confirmedHardToRead);
    revision.current = ex.revision;
    setConflict(false);
  };

  useEffect(() => {
    let alive = true;
    Promise.all([clinicianApi.latestExtraction(patient.id), patient.lastCheckId ? clinicianApi.check(patient.lastCheckId) : Promise.resolve(null)]).then(
      ([{ extraction: ex }, last]) => {
        if (!alive) return;
        if (ex) adopt(ex);
        setCheck(last);
        setStep(last && ex && !ex.stale && last.extractionId === ex.id ? "findings" : ex && !ex.stale ? "review" : "records");
        setReady(true);
      },
      (e) => {
        if (alive) {
          setError(errorText(e));
          setReady(true);
        }
      },
    );
    return () => {
      alive = false;
    };
    // Load once per patient; later changes come from this tab's own actions.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [patient.id]);

  const save = (next: { review?: Review; coverage?: CoverageReview; acknowledged?: boolean; confirmed?: string[] }) => {
    if (!extraction) return;
    const body = { review: next.review ?? review, coverage: next.coverage ?? coverage, acknowledged: next.acknowledged ?? acknowledged, confirmed: next.confirmed ?? confirmed };
    if (next.review) setReview(next.review);
    if (next.coverage) setCoverage(next.coverage);
    if (next.acknowledged !== undefined) setAcknowledged(next.acknowledged);
    if (next.confirmed) setConfirmed(next.confirmed);
    if (saveTimer.current) clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(() => {
      saving.current = saving.current.then(async () => {
        try {
          const saved = await clinicianApi.saveDecisions(extraction.id, {
            revision: revision.current,
            decisions: body.review,
            coverageReview: body.coverage,
            acknowledgedOriginals: body.acknowledged,
            confirmedHardToRead: body.confirmed,
          });
          revision.current = saved.revision;
        } catch (e) {
          if (e instanceof ApiError && e.code === "STALE_REVISION") setConflict(true);
          else setError(errorText(e));
        }
      });
    }, 400);
  };
  const flush = async () => {
    await saving.current;
    if (!saveTimer.current || !extraction) return;
    clearTimeout(saveTimer.current);
    saveTimer.current = null;
    const saved = await clinicianApi.saveDecisions(extraction.id, {
      revision: revision.current,
      decisions: review,
      coverageReview: coverage,
      acknowledgedOriginals: acknowledged,
      confirmedHardToRead: confirmed,
    });
    revision.current = saved.revision;
  };
  const read = async () => {
    setBusy("Reading the records");
    setError("");
    try {
      adopt(await clinicianApi.startExtraction(patient.id));
      setStep("review");
    } catch (e) {
      setError(errorText(e));
    } finally {
      setBusy("");
    }
  };
  const loadLatest = async () => {
    if (!extraction) return;
    adopt(await clinicianApi.extraction(extraction.id));
  };
  const run = async () => {
    if (!extraction) return;
    setBusy("Running the check");
    setError("");
    try {
      await flush();
      const result = await clinicianApi.runCheck(patient.id, { extractionId: extraction.id, revision: revision.current });
      setCheck(result);
      setStep("findings");
      window.scrollTo({ top: 0 });
      onChanged();
    } catch (e) {
      if (e instanceof ApiError && e.code === "STALE_REVISION") setConflict(true);
      setError(errorText(e));
    } finally {
      setBusy("");
    }
  };

  if (!ready) return <Spinner label="Opening the check" />;
  const decided = extraction ? extraction.facts.filter((f) => review[f.id]).length : 0;
  const steps: { id: Step; title: string; sub: string; done: boolean; enabled: boolean }[] = [
    { id: "records", title: "Records", sub: extraction?.stale ? "Changed since the last reading" : "Add and read", done: !!extraction && !extraction.stale, enabled: true },
    { id: "review", title: "Review", sub: extraction ? `${decided} of ${extraction.facts.length} decided` : "Confirm each fact", done: !!check && check.extractionId === extraction?.id, enabled: !!extraction && !extraction.stale },
    { id: "findings", title: "Findings", sub: check ? plural(check.assessment.contextual.findings.length, "finding") : "With and without the patient's details", done: false, enabled: !!check },
  ];
  return (
    <div className="check-tab flex flex-col gap-4">
      {step !== "findings" && <NewInformation patient={patient} action={false} />}
      <ol className="m-0 grid list-none grid-cols-1 gap-1 rounded-2xl bg-quiet p-1 sm:grid-cols-3" aria-label="Check steps">
        {steps.map((s, i) => {
          const current = step === s.id;
          return (
            <li key={s.id}>
              <button
                type="button"
                aria-current={current ? "step" : undefined}
                disabled={!s.enabled || !!busy}
                onClick={() => setStep(s.id)}
                className={cn(
                  "flex h-12 w-full cursor-pointer items-center gap-2.5 rounded-xl border-0 px-3 text-left font-sans outline-none transition-[background-color,box-shadow,color] focus-visible:shadow-[0_0_0_2px_var(--color-brand)] disabled:cursor-not-allowed disabled:opacity-50",
                  current ? "bg-page text-ink shadow-chip" : "bg-transparent text-muted hover:text-ink",
                )}
              >
                <span
                  className={cn(
                    "flex size-[22px] shrink-0 items-center justify-center rounded-full font-mono text-[11px] font-medium",
                    current ? "bg-ink text-white" : s.done ? "bg-green-fill text-green-text" : "bg-page text-muted shadow-[inset_0_0_0_1px_var(--color-line)]",
                  )}
                >
                  {s.done && !current ? <Icon name="check-line" size={13} /> : i + 1}
                </span>
                <span className="flex min-w-0 flex-col gap-0.5 leading-tight">
                  <strong className="text-[13px] font-medium">{s.title}</strong>
                  <small className="truncate text-[12px] text-muted">{s.sub}</small>
                </span>
              </button>
            </li>
          );
        })}
      </ol>
      {error && (
        <Notice tone="red" role="alert" icon="error-warning-line">
          {error}
        </Notice>
      )}
      {conflict && (
        <Notice tone="amber" role="alert" icon="refresh-line" actions={<Button size="sm" onClick={() => void loadLatest()}>Load the latest</Button>}>
          This review was changed in another tab. Load the latest version before you go on; your last change here was not saved.
        </Notice>
      )}
      {busy && <Spinner label={busy} />}
      {step === "records" && (
        <>
          <RecordsPanel patient={patient} onChanged={() => extraction && setExtraction({ ...extraction, stale: true })} />
          {extraction && (
            <div className="flex flex-wrap gap-2" aria-label="Reading progress">
              {extraction.records.map((r) => {
                const look = readLook[r.status] ?? { label: "Reading", tone: "amber" as const, icon: "loader-4-line" as const };
                return (
                  <StatusPill key={r.recordId} size="sm" tone={look.tone} icon={look.icon}>
                    {r.title} · {look.label}
                  </StatusPill>
                );
              })}
            </div>
          )}
          <div className="actionbar">
            <div className="actionbar-inner">
              <span className="hint">{extraction?.stale ? "The records changed. Read them again before the check." : "Context reads each record and links every fact to its line."}</span>
              <Button variant="primary" disabled={!!busy} onClick={() => void read()}>
                {extraction ? "Read the records again" : "Read records"}
              </Button>
            </div>
          </div>
        </>
      )}
      {step === "review" && extraction && (
        <ReviewStep
          patient={patient}
          extraction={extraction}
          review={review}
          coverage={coverage}
          acknowledged={acknowledged}
          confirmed={confirmed}
          onConfirm={(next) => save({ confirmed: next })}
          busy={!!busy || conflict}
          onReview={(next) => save({ review: next })}
          onCoverage={(next) => save({ coverage: next })}
          onAcknowledge={(value) => save({ acknowledged: value })}
          onClarified={(ex) => adopt(ex)}
          onRun={() => void run()}
        />
      )}
      {step === "findings" && check && <FindingsView patient={patient} check={check} onCheck={setCheck} />}
    </div>
  );
}
