import { useEffect, useRef, useState } from "react";
import { clinicianApi } from "../../api/client";
import type { ContextRecord, PatientDetail } from "../../api/types";
import { contentTypeOf, fileProblem, guessType, RECORD_LIMITS, roomFor, titleFromFile } from "../../logic/records";
import { Count, EmptyState, SectionHeader } from "../kit";
import { useLocation } from "../router";
import { errorText, ErrorNote, Spinner, useLoad, useToast } from "../ui";
import { AddRecords, type Pending } from "./AddRecords";
import { EditRecord, PasteNote, RemoveRecord } from "./RecordDialogs";
import { RecordsTable } from "./RecordsTable";
import { SourceDrawer, type SourceRef } from "./SourceDrawer";

/** REC-1 to REC-4: the patient's records, and adding more from anywhere. */
export function RecordsPanel({ patient, onChanged }: { patient: PatientDetail; onChanged?: () => void }) {
  const records = useLoad(() => clinicianApi.records(patient.id), [patient.id]);
  const [pending, setPending] = useState<Pending[]>([]);
  const [notice, setNotice] = useState("");
  const [busy, setBusy] = useState(false);
  const [paste, setPaste] = useState(false);
  const [editing, setEditing] = useState<ContextRecord | null>(null);
  const [removing, setRemoving] = useState<ContextRecord | null>(null);
  const [source, setSource] = useState<SourceRef | null>(null);
  const toast = useToast();
  const chooseButton = useRef<HTMLButtonElement>(null);
  const list = records.data?.records ?? [];
  // "Add records" in the header lands here with ?add=1: bring the add panel into view and focus it.
  const wantsAdd = useLocation().query.get("add") === "1";
  useEffect(() => {
    if (!wantsAdd) return;
    chooseButton.current?.closest("[data-add-records]")?.scrollIntoView({ block: "start" });
    chooseButton.current?.focus({ preventScroll: true });
  }, [wantsAdd]);
  // REC-7 and failed reads: confirm a held-back record, remove it, or read a failed file again.
  const act = async (work: () => Promise<unknown>, done: string) => {
    try {
      await work();
      records.reload(true);
      onChanged?.();
      toast.show(done);
    } catch (e) {
      toast.show(errorText(e), true);
    }
  };

  const choose = (files: FileList | File[], origin: "upload" | "camera") => {
    const all = [...files];
    const { fits, message } = roomFor(list.length + pending.length, all.length);
    setNotice(message ?? "");
    setPending((p) => [
      ...p,
      ...all.slice(0, fits).map((file, i) => ({
        key: `${Date.now()}-${i}-${file.name}`,
        file,
        text: null,
        title: origin === "camera" ? "Photo of a record" : titleFromFile(file.name),
        type: guessType(file.name),
        place: "",
        recordDate: "",
        origin,
        error: fileProblem(file),
      })),
    ]);
  };
  const update = (key: string, change: Partial<Pending>) => setPending((p) => p.map((x) => (x.key === key ? { ...x, ...change } : x)));

  const addAll = async () => {
    setBusy(true);
    const failed: Pending[] = [];
    for (const item of pending) {
      if (item.error) {
        failed.push(item);
        continue;
      }
      try {
        let uploadId: string | null = null;
        if (item.file) {
          const ticket = await clinicianApi.requestUpload(patient.id, { fileName: item.file.name, contentType: contentTypeOf(item.file), sizeBytes: item.file.size });
          await clinicianApi.uploadFile(ticket, item.file);
          uploadId = ticket.uploadId;
        }
        await clinicianApi.addRecord(patient.id, {
          uploadId,
          text: item.text,
          title: item.title.trim() || "Untitled record",
          type: item.type,
          place: item.place.trim() || null,
          recordDate: item.recordDate || null,
          origin: item.origin,
        });
      } catch (e) {
        failed.push({ ...item, error: errorText(e) });
      }
    }
    setPending(failed);
    setBusy(false);
    records.reload(true);
    onChanged?.();
    if (!failed.length) toast.show("Records added. Read them again on the Check tab.");
  };

  return (
    <div className="flex flex-col gap-8">
      {toast.node}
      <section className="flex flex-col gap-3" aria-labelledby={`records-title-${patient.id}`}>
        <SectionHeader
          title={
            <>
              <span id={`records-title-${patient.id}`}>Records</span>
              <Count>
                {list.length} of {RECORD_LIMITS.perCheck}
              </Count>
            </>
          }
        />
        {records.loading && !records.data && <Spinner />}
        <ErrorNote error={records.error} onRetry={() => records.reload()} />
        {list.length > 0 && (
          <RecordsTable
            records={list}
            patientFirstName={patient.firstName}
            actions={{
              open: (r) => setSource({ recordId: r.id, title: r.title, lines: [] }),
              edit: setEditing,
              remove: setRemoving,
              confirm: (r) => void act(() => clinicianApi.confirmRecordPatient(r.id), "Confirmed. Read the records again on the Check tab."),
              readAgain: (r) => void act(() => clinicianApi.readRecordAgain(r.id), "Reading it again."),
            }}
          />
        )}
        {records.data && !list.length && <EmptyState title="No records yet." body="Add the patient's prescriptions, results and notes below." />}
      </section>

      <AddRecords
        pending={pending}
        notice={notice}
        busy={busy}
        chooseRef={chooseButton}
        onChoose={choose}
        onPaste={() => setPaste(true)}
        onUpdate={update}
        onRemove={(key) => setPending((all) => all.filter((x) => x.key !== key))}
        onClear={() => setPending([])}
        onAddAll={() => void addAll()}
      />
      <PasteNote
        open={paste}
        onClose={() => setPaste(false)}
        onAdd={(text, title) => {
          setPaste(false);
          setPending((p) => [...p, { key: `paste-${Date.now()}`, file: null, text, title, type: "clinic_note", place: "", recordDate: "", origin: "pasted", error: text.length > 12000 ? "This note is longer than 12,000 characters. Split it into smaller records." : null }]);
        }}
      />
      <EditRecord record={editing} onClose={() => setEditing(null)} onSaved={() => { setEditing(null); records.reload(true); }} />
      <RemoveRecord
        record={removing}
        onClose={() => setRemoving(null)}
        onRemove={(r) => {
          setRemoving(null);
          void act(() => clinicianApi.removeRecord(r.id), "Record removed. Its facts left review and the next check.");
        }}
      />
      <SourceDrawer source={source} onClose={() => setSource(null)} />
    </div>
  );
}

export function RecordsTab({ patient }: { patient: PatientDetail }) {
  return <RecordsPanel patient={patient} />;
}
