import { useState, useEffect, Fragment } from "react";
import { useAuth } from "@clerk/react";
import { VerdictBadge } from "./Badge";
import StatusLegend from "./StatusLegend";
import ConfirmVisitModal from "./ConfirmVisitModal";
import { updateDocStatus } from "../lib/api";

// P6 patient-nested worklist (Kisa-approved mockup, 2026-07-07).
// One row per patient carrying the rollup verdict; expands into device
// coverage lines with their doc checklists and rule citations. Green is
// earned only from the readiness verdict. Tabs count PATIENTS.
// The five status labels are locked; "Data gap" is a filter + chip, never
// a sixth label (Kisa Decision E).

const DOC_LABELS = {
  swo: "SWO",
  visit: "Qualifying Visit",
  pecos: "PECOS Enrollment",
  pa: "Prior Authorization",
  diagnosis: "Diagnosis on File",
};

// doc_type -> the canonical CSV field whose mapping decides whether an
// absent item is a FILE gap (column never mapped) or a PATIENT gap.
const DOC_COLUMN = {
  swo: "csv_swo_status",
  visit: "csv_visit_date",
  pecos: "csv_pecos_verified",
  pa: "csv_pa_status",
  diagnosis: "csv_diagnosis_on_file",
};

const SWO_CYCLE = ["pending", "requested", "on_file"];
const PA_CYCLE = ["not_required", "requested", "approved", "denied"];
const DOC_STATUS_LABELS = {
  pending: "Pending", requested: "Requested", on_file: "On File",
  not_required: "Not Required", approved: "Approved", denied: "Denied",
};

// Timing hints are descriptive phrases, never status labels (locked sets).
const TIMING_HINTS = {
  SUPPLY_LAPSED: "Supply lapsed",
  VISIT_REQUIRED: "Visit overdue",
  RENEWAL_CRITICAL: "Visit renewal critical",
  RENEWAL_ELEVATED: "Visit renewal due soon",
  RENEWAL_SOON: "Visit renewal approaching",
  RESUPPLY_READY: "In resupply window",
  ACTIVE: "Timing on track",
  NO_RECENT_SHIPMENT: "No recent shipment",
  TRANSFER_PENDING: "Transfer pending",
};

const TABS = [
  { key: "all", label: "All Patients", color: "#5A7E7E" },
  { key: "At Risk", label: "At Risk", color: "#CC2222" },
  { key: "Action Needed", label: "Action Needed", color: "#CB6B20" },
  { key: "On Track", label: "On Track", color: "#2EA3A3" },
  { key: "Clear to Ship", label: "Clear to Ship", color: "#22C55E", check: true },
  { key: "Plan Type Needed", label: "Plan Type Needed", color: "#9B8EC4" },
];

function itemIsFileGap(item, mappedFields) {
  // REQUIRED only: NOT_EVALUATED items (plan type unknown) belong to the
  // Plan Type Needed treatment, never double-flagged as data gaps.
  return (
    item.input_state === "ABSENT" &&
    item.required_state === "REQUIRED" &&
    !(DOC_COLUMN[item.doc_type] in mappedFields)
  );
}

function patientFileGaps(p, mappedFields) {
  const gaps = new Set();
  for (const line of p.lines || []) {
    for (const item of line.doc_items || []) {
      if (itemIsFileGap(item, mappedFields)) gaps.add(item.doc_type);
    }
  }
  return [...gaps];
}

function lineToEcho(line) {
  const by = {};
  (line.doc_items || []).forEach((i) => { by[i.doc_type] = i.value; });
  return {
    device_type: line.device_type,
    gradeable: line.gradeable,
    plan_type: line.plan_type,
    shipment_date: line.shipment_date || null,
    csv_swo_status: by.swo || null,
    csv_visit_date: by.visit || null,
    csv_pecos_verified: by.pecos || null,
    csv_pa_status: by.pa || null,
    csv_diagnosis_on_file: by.diagnosis || null,
  };
}

function lineToModalRecord(p, line) {
  const by = {};
  (line.doc_items || []).forEach((i) => { by[i.doc_type] = i.value; });
  return {
    patient_id: p.patient_id,
    last_shipment_date: line.shipment_date,
    payer: line.payer || "",
    device_type: line.device_type,
    quantity: line.total_quantity || 1,
    plan_type: line.plan_type,
    csv_visit_date: by.visit || null,
    csv_swo_status: by.swo || null,
    csv_pecos_verified: by.pecos || null,
    csv_pa_status: by.pa || null,
    csv_diagnosis_on_file: by.diagnosis || null,
    _echo_lines: (p.lines || []).map(lineToEcho),
  };
}

// Merge a P4 recompute response ({lines, rollup_status}) into local state.
// Line identity is (device_type, plan_type) — a patient can hold the same
// device under two plan types (typed + unknown), so device alone is not a key.
function mergeRecompute(p, recomputed) {
  if (!recomputed?.lines) return p;
  const lineKey = (l) => `${l.device_type}:${l.plan_type ?? ""}`;
  const byKey = {};
  recomputed.lines.forEach((l) => { byKey[lineKey(l)] = l; });
  return {
    ...p,
    rollup_status: recomputed.rollup_status ?? p.rollup_status,
    lines: p.lines.map((line) => {
      const upd = byKey[lineKey(line)];
      return upd
        ? { ...line, line_status: upd.line_status, doc_items: upd.doc_items ?? line.doc_items }
        : line;
    }),
  };
}

export default function PatientWorklist({ data, onImportClick }) {
  const { getToken } = useAuth();
  const [patients, setPatients] = useState(data.patients || []);
  const [tab, setTab] = useState("all");
  const [refine, setRefine] = useState(null); // null | "gaps" | "outreach"
  const [expanded, setExpanded] = useState(null); // patient_id
  const [confirming, setConfirming] = useState(null); // {record, patientId}
  const [showAllSkipped, setShowAllSkipped] = useState(false);

  useEffect(() => {
    setPatients(data.patients || []);
    setExpanded(null);
  }, [data]);

  const mappedFields = data.mapping_summary?.mapped || {};
  const skippedReasons = data.skipped_reasons || [];
  const skippedCount = data.skipped || 0;

  const gradeable = patients.filter((p) => p.rollup_status);
  const gapPatients = patients.filter(
    (p) => patientFileGaps(p, mappedFields).length > 0
  );
  const outreachPatients = patients.filter(
    (p) =>
      ["At Risk", "Action Needed"].includes(p.rollup_status) &&
      patientFileGaps(p, mappedFields).length === 0
  );

  // Single derivation for all counts: always from the local patients state.
  const counts = { all: patients.length };
  TABS.slice(1).forEach((t) => {
    counts[t.key] = gradeable.filter((p) => p.rollup_status === t.key).length;
  });

  let visible =
    tab === "all" ? patients : patients.filter((p) => p.rollup_status === tab);
  if (refine === "gaps") {
    visible = visible.filter((p) => patientFileGaps(p, mappedFields).length > 0);
  } else if (refine === "outreach") {
    visible = visible.filter((p) => outreachPatients.includes(p));
  }

  function downloadSkipped() {
    const blob = new Blob(
      ["Signal — rows not processed\n\n" + skippedReasons.join("\n") + "\n"],
      { type: "text/plain;charset=utf-8;" }
    );
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `signal-not-processed-${new Date().toISOString().slice(0, 10)}.txt`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }

  function applyRecompute(patientId, recomputed) {
    setPatients((prev) =>
      prev.map((p) => (p.patient_id === patientId ? mergeRecompute(p, recomputed) : p))
    );
  }

  const ungraded = patients.length - gradeable.length;

  return (
    <div className="bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] shadow-[var(--shadow-card-md)] overflow-hidden transition-colors">
      {/* Header + patient tabs */}
      <div className="flex items-center justify-between px-[22px] py-4 border-b border-[var(--border-color)] bg-[var(--bg-elevated)] gap-4 flex-wrap">
        <div>
          <div className="font-heading font-bold text-[15px] text-[var(--text-heading)]">
            Outreach Worklist
          </div>
          <div className="font-mono text-[11px] text-[var(--text-warm)] mt-[2px]">
            {patients.length} patients · {gradeable.length} gradeable
            {ungraded > 0 && ` · ${ungraded} informational only`}
            {skippedCount > 0 && ` · ${skippedCount} rows not processed`}
          </div>
        </div>
        <div className="flex gap-[6px] flex-wrap">
          {TABS.map((t) => {
            const count = counts[t.key] ?? 0;
            const pct =
              t.key !== "all" && gradeable.length > 0
                ? Math.round((count / gradeable.length) * 100)
                : null;
            const isActive = tab === t.key;
            const indicatorColor = isActive ? "white" : t.color;
            return (
              <button
                key={t.key}
                onClick={() => setTab(t.key)}
                className={`px-3 py-[5px] rounded-full text-[11.5px] cursor-pointer font-body transition-all border inline-flex items-center gap-[5px] ${
                  isActive
                    ? "bg-[var(--brand)] text-white border-[var(--brand)]"
                    : "bg-transparent border-[var(--border-color)] text-[var(--text-secondary)] hover:border-[var(--brand)] hover:text-[var(--brand)]"
                }`}
              >
                {t.check ? (
                  <span style={{ color: indicatorColor, fontSize: "12px", fontWeight: 800, lineHeight: 1 }}>✓</span>
                ) : (
                  <span style={{ width: 6, height: 6, borderRadius: "50%", backgroundColor: indicatorColor, display: "inline-block", flexShrink: 0 }} />
                )}
                {t.label}
                <span style={{ opacity: 0.65, fontSize: "10.5px", fontWeight: 500 }}>
                  {count}
                  {pct !== null ? ` · ${pct}%` : ""}
                </span>
              </button>
            );
          })}
        </div>
      </div>

      {/* Refine pills — separates fix-the-file work from outreach work */}
      <div className="flex items-center gap-[10px] px-[22px] py-[8px] border-b border-[var(--border-subtle)] text-[11px] text-[var(--text-warm)]">
        Refine:
        <button
          onClick={() => setRefine(refine === "gaps" ? null : "gaps")}
          className={`inline-flex items-center gap-[5px] rounded-full px-[11px] py-[2px] text-[11px] font-semibold border cursor-pointer ${
            refine === "gaps"
              ? "bg-[#6B4F9A] text-white border-[#6B4F9A]"
              : "bg-[rgba(122,94,160,0.10)] text-[#6B4F9A] border-[#9B8EC4]"
          }`}
        >
          ◇ Data gaps <b>{gapPatients.length}</b>
        </button>
        <button
          onClick={() => setRefine(refine === "outreach" ? null : "outreach")}
          className={`inline-flex items-center gap-[5px] rounded-full px-[11px] py-[2px] text-[11px] font-semibold border cursor-pointer ${
            refine === "outreach"
              ? "bg-[var(--brand)] text-white border-[var(--brand)]"
              : "border-[var(--border-color)] text-[var(--text-secondary)]"
          }`}
        >
          Outreach only <b>{outreachPatients.length}</b>
        </button>
        <span className="ml-auto italic text-[10.5px]">
          Data-gap patients keep their honest status; this separates fix-the-file work from call-the-prescriber work
        </span>
      </div>

      {/* Not-processed strip — rows Signal never scores and never hides */}
      {skippedCount > 0 && (
        <div className="flex items-start gap-3 px-[22px] py-[10px] border-b border-[var(--border-subtle)] bg-[rgba(203,107,32,0.05)] text-[12px] text-[var(--text-secondary)]">
          <div className="flex-1">
            <b className="text-[var(--text-heading)]">
              {skippedCount} row{skippedCount !== 1 ? "s" : ""} not processed
            </b>{" "}
            — Signal never scores a row whose required information was not provided, and never hides one:
            <ul className="list-disc ml-5 mt-1">
              {(showAllSkipped ? skippedReasons : skippedReasons.slice(0, 3)).map((r, i) => (
                <li key={i}>{r}</li>
              ))}
            </ul>
            {skippedReasons.length > 3 && !showAllSkipped && (
              <button className="text-[var(--brand)] font-semibold mt-1 cursor-pointer bg-transparent border-none p-0" onClick={() => setShowAllSkipped(true)}>
                Show all {skippedReasons.length}
              </button>
            )}
          </div>
          <button onClick={downloadSkipped} className="text-[var(--brand)] font-semibold whitespace-nowrap cursor-pointer bg-transparent border-none p-0">
            Download list ↓
          </button>
        </div>
      )}

      <StatusLegend extended />

      {/* Patient table */}
      <table className="w-full border-collapse">
        <thead>
          <tr>
            {["", "Patient", "Device(s)", "Payer", "Status", "Last Resupply", "Actions"].map((h, i) => (
              <th key={i} className="px-[18px] py-[10px] text-left text-[10.5px] font-semibold tracking-[0.06em] uppercase text-[var(--text-warm)] bg-[var(--bg-elevated)] border-b border-[var(--border-color)] whitespace-nowrap">
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {visible.map((p) => {
            const isExpanded = expanded === p.patient_id;
            const gaps = patientFileGaps(p, mappedFields);
            const isPTN = p.rollup_status === "Plan Type Needed";
            const capped = p.plan_type_needed && !isPTN;
            const lastShip = (p.lines || [])
              .map((l) => l.shipment_date)
              .filter(Boolean)
              .sort()
              .pop();
            return (
              <Fragment key={p.patient_id}>
                <tr
                  data-patient-row={p.patient_id}
                  className={`border-b border-[var(--border-subtle)] cursor-pointer transition-colors hover:bg-[var(--row-hover)] ${
                    !p.rollup_status ? "opacity-60" : ""
                  } ${isExpanded ? "bg-[rgba(20,122,122,0.04)]" : ""}`}
                  onClick={() => setExpanded(isExpanded ? null : p.patient_id)}
                >
                  <td className="pl-[18px] py-[13px] w-[26px] text-[9px] text-[var(--text-warm)] align-middle">
                    {isExpanded ? "▼" : "▶"}
                  </td>
                  <td className="px-[18px] py-[13px] align-middle">
                    <span className="font-mono text-[12.5px] font-bold text-[var(--brand)]">
                      {p.patient_id}
                    </span>
                  </td>
                  <td className="px-[18px] py-[13px] text-[13px] text-[var(--text-primary)] align-middle">
                    {(p.lines || []).map((l) => l.device_display).join(", ")}
                  </td>
                  <td className="px-[18px] py-[13px] text-[12.5px] text-[var(--text-secondary)] align-middle">
                    {(p.lines || [])[0]?.payer || "—"}
                  </td>
                  <td className="px-[18px] py-[13px] align-middle">
                    <VerdictBadge status={p.rollup_status} />
                    {capped && (
                      <div className="text-[10px] italic text-[var(--text-warm)] mt-[3px]">
                        Capped: includes a Plan Type Needed line
                      </div>
                    )}
                    {gaps.length > 0 && !isPTN && (
                      <div className="text-[10px] italic text-[#6B4F9A] mt-[3px]">
                        Data gap — {gaps.map((g) => DOC_LABELS[g]).join(", ")} column not provided in the file
                      </div>
                    )}
                    {!p.rollup_status && (
                      <div className="text-[10px] italic text-[var(--text-warm)] mt-[3px]">
                        Non-CGM device — displayed, not graded, excluded from counts
                      </div>
                    )}
                  </td>
                  <td className="px-[18px] py-[13px] font-mono text-[12px] text-[var(--text-warm)] align-middle">
                    {lastShip || "—"}
                  </td>
                  <td className="px-[18px] py-[13px] align-middle" onClick={(e) => e.stopPropagation()}>
                    {isPTN && (
                      <button
                        onClick={onImportClick}
                        className="inline-flex items-center gap-[5px] text-[11px] font-semibold text-[#6B4F9A] bg-[rgba(122,94,160,0.10)] border border-[#9B8EC4] rounded-[5px] px-[11px] py-[4px] cursor-pointer"
                      >
                        ◆ Map the plan type
                      </button>
                    )}
                    {gaps.length > 0 && !isPTN && (
                      <span className="inline-flex items-center gap-[4px] text-[10px] font-bold text-[#6B4F9A] bg-[rgba(122,94,160,0.10)] border border-[#9B8EC4] rounded-[4px] px-[7px] py-[1px]">
                        ◇ Fix the file, not the phone
                      </span>
                    )}
                  </td>
                </tr>

                {isExpanded &&
                  (p.lines || []).map((line) => (
                    <tr key={`${p.patient_id}:${line.device_type}:${line.plan_type}`} className="bg-[var(--bg-elevated)] border-b border-[var(--border-subtle)]">
                      <td colSpan={7} className="py-0">
                        <DeviceLine
                          patient={p}
                          line={line}
                          mappedFields={mappedFields}
                          getToken={getToken}
                          onRecomputed={(rec) => applyRecompute(p.patient_id, rec)}
                          onConfirmVisit={() =>
                            setConfirming({ record: lineToModalRecord(p, line), patientId: p.patient_id })
                          }
                        />
                      </td>
                    </tr>
                  ))}
              </Fragment>
            );
          })}
          {visible.length === 0 && (
            <tr>
              <td colSpan={7} className="px-[22px] py-8 text-center text-[var(--text-warm)]">
                No patients match this filter.
              </td>
            </tr>
          )}
        </tbody>
      </table>

      {/* Footer */}
      <div className="flex items-center justify-between px-[22px] py-3 border-t border-[var(--border-subtle)] text-[11px] text-[var(--text-warm)] bg-[var(--bg-elevated)]">
        <span className="text-[var(--text-secondary)]">
          PHI-safe — patient names and DOBs never stored. Crosswalk: patient_id only.
        </span>
        <span>
          {visible.length} of {patients.length} patients shown
        </span>
      </div>

      {confirming && (
        <ConfirmVisitModal
          record={confirming.record}
          onClose={() => setConfirming(null)}
          onConfirmed={(patientId, updated) => applyRecompute(patientId, updated)}
        />
      )}
    </div>
  );
}

function DeviceLine({ patient, line, mappedFields, getToken, onRecomputed, onConfirmVisit }) {
  const items = line.doc_items || [];
  const visitItem = items.find((i) => i.doc_type === "visit");
  const showConfirmVisit = line.gradeable && visitItem && !visitItem.satisfied;
  return (
    <div className="pl-[56px] pr-[22px] py-[11px] border-l-4 border-[rgba(20,122,122,0.16)] ml-[18px]">
      <div className="flex items-center gap-[10px] mb-[8px]">
        <span className="text-[12px] font-bold text-[var(--text-primary)]">{line.device_display}</span>
        <span className="text-[10.5px] text-[var(--text-warm)] bg-[var(--bg-card)] border border-[var(--border-color)] rounded px-[7px]">
          {line.plan_type || "Plan type needed"}
        </span>
        <VerdictBadge status={line.line_status} />
        {line.timing_flag && (
          <span className="text-[10.5px] text-[var(--text-warm)] italic">
            {TIMING_HINTS[line.timing_flag] || line.timing_flag}
          </span>
        )}
        <span className="ml-auto font-mono text-[10px] text-[var(--text-warm)]">
          {line.order_numbers?.length ? `Orders: ${line.order_numbers.join(", ")} · ` : ""}
          {line.hcpcs_codes?.length ? `HCPCS: ${line.hcpcs_codes.join(", ")} · ` : ""}
          qty {line.total_quantity}
          {line.prior_shipment_count > 0 && ` · ${line.prior_shipment_count} prior shipment${line.prior_shipment_count !== 1 ? "s" : ""}`}
        </span>
      </div>
      {line.gradeable && items.length > 0 ? (
        <div className="flex flex-wrap gap-y-[6px]">
          {items.map((item) => (
            <ChecklistItem
              key={item.doc_type}
              patient={patient}
              line={line}
              item={item}
              fileGap={itemIsFileGap(item, mappedFields)}
              getToken={getToken}
              onRecomputed={onRecomputed}
            />
          ))}
          {showConfirmVisit && (
            <button
              onClick={onConfirmVisit}
              className="self-start ml-auto bg-transparent border border-[var(--accent)] text-[var(--accent-text)] px-[10px] py-[3px] rounded-md text-[11px] cursor-pointer whitespace-nowrap hover:bg-[rgba(203,107,32,0.1)]"
            >
              Confirm Visit →
            </button>
          )}
        </div>
      ) : (
        <div className="text-[11px] italic text-[var(--text-warm)]">
          Non-CGM device — no readiness actions.
        </div>
      )}
    </div>
  );
}

function ChecklistItem({ patient, line, item, fileGap, getToken, onRecomputed }) {
  const [saving, setSaving] = useState(false);
  const cyclable = item.doc_type === "swo" ? SWO_CYCLE : item.doc_type === "pa" ? PA_CYCLE : null;

  const icon = fileGap
    ? { glyph: "◇", cls: "text-[#6B4F9A]" }
    : item.satisfied
    ? item.required_state === "NOT_REQUIRED" && item.input_state === "ABSENT"
      ? { glyph: "—", cls: "text-[var(--text-warm)]" }
      : { glyph: "✓", cls: "text-[#166534]" }
    : item.quality === "BAD"
    ? { glyph: "✗", cls: "text-[#CC2222]" }
    : { glyph: "✗", cls: "text-[#CB6B20]" };

  const valueText = fileGap
    ? "Column not mapped"
    : item.value ||
      (item.required_state === "NOT_REQUIRED" ? "Not required" : "Not on file");

  async function handleCycle() {
    if (!cyclable || saving || fileGap) return;
    const currentKey = Object.entries(DOC_STATUS_LABELS).find(([, v]) => v === item.value)?.[0];
    const idx = currentKey ? cyclable.indexOf(currentKey) : -1;
    const nextKey = cyclable[(idx + 1) % cyclable.length];
    setSaving(true);
    try {
      const token = getToken ? await getToken().catch(() => null) : null;
      const result = await updateDocStatus(
        patient.patient_id,
        item.doc_type,
        nextKey,
        null,
        null,
        token,
        line.device_type,
        (patient.lines || []).map(lineToEcho)
      );
      if (result?.lines) onRecomputed(result);
    } finally {
      setSaving(false);
    }
  }

  return (
    <div className="flex items-start gap-[6px] basis-1/5 min-w-[170px] pr-[12px]">
      <span className={`text-[12px] font-extrabold w-[14px] text-center ${icon.cls}`}>{icon.glyph}</span>
      <div>
        <div className="text-[11px] font-semibold text-[var(--text-primary)]">
          {DOC_LABELS[item.doc_type]}
        </div>
        {cyclable && !fileGap ? (
          <button
            onClick={handleCycle}
            disabled={saving}
            title={`Click to update ${DOC_LABELS[item.doc_type]} status (this device only)`}
            className={`text-[11px] font-medium bg-transparent border-none p-0 text-left cursor-pointer hover:underline underline-offset-2 disabled:opacity-50 ${
              item.satisfied ? "text-[#166534]" : "text-[#CB6B20]"
            }`}
          >
            {saving ? "Saving…" : valueText}
          </button>
        ) : (
          <div className={`text-[11px] ${fileGap ? "text-[#6B4F9A] font-semibold" : item.satisfied ? "text-[var(--text-secondary)]" : "text-[#CB6B20] font-medium"}`}>
            {valueText}
          </div>
        )}
        <div className="text-[9.5px] text-[var(--text-warm)]">
          {item.rule_id ? `${item.rule_id}` : ""}
          {fileGap ? " · resolves at re-import or mapping review" : ""}
        </div>
      </div>
    </div>
  );
}
