import { useEffect, useMemo, useRef, useState } from "react";
import { clinicianApi } from "../../api/client";
import { followCheckIn, type StreamMode } from "../../api/events";
import type { CheckIn, PatientDetail } from "../../api/types";
import { applyEvents, isEnded, isLive } from "../../logic/checkin-events";
import { clock, dayLabel, firstName } from "../../logic/format";
import { checkInPill } from "../../logic/pills";
import { notReachedAlert } from "../../logic/urgent";
import { clinicCloseToday, zoneOffset } from "../../logic/windows";
import { Button, Callout, cn, Icon, linkButtonClass } from "../kit";
import { askForNotifications, notificationsAllowed, notificationsSupported, notify } from "../notify";
import { tabPath } from "../patient/links";
import { Pill } from "../Pill";
import { Link, navigate } from "../router";
import { alertsChanged } from "../Shell";
import { shortName, useClinician } from "../session";
import { errorText, ErrorNote, Spinner } from "../ui";
import { CallText, NotesList, StatusTrail, SummaryCard, UrgentBox } from "./LiveParts";

const tel = (phone: string) => `tel:${phone.replace(/[^\d+]/g, "")}`;
const onRed = "bg-page text-red-text hover:bg-white/80 no-underline";

/** YCI-23 to YCI-29: "Check-in call · Live", notes as they arrive, the summary and the call text. */
export function LivePanel({ patient, checkInId, onChanged }: { patient: PatientDetail; checkInId: string; onChanged: () => void }) {
  const { me, clinic } = useClinician();
  const [call, setCall] = useState<CheckIn | null>(null);
  const [error, setError] = useState<unknown>(null);
  const [mode, setMode] = useState<StreamMode>("closed");
  const [picked, setPicked] = useState<string[]>([]);
  const [adding, setAdding] = useState(false);
  const [added, setAdded] = useState(0);
  const [lit, setLit] = useState<string | null>(null);
  const [notifyOn, setNotifyOn] = useState(notificationsAllowed() && me.notifications.browser);
  const prev = useRef<CheckIn | null>(null);
  const first = firstName(patient.name);
  const doctor = shortName(me);

  useEffect(() => {
    let sub: { close: () => void } | null = null;
    let alive = true;
    clinicianApi.checkIn(checkInId).then(
      (snapshot) => {
        if (!alive) return;
        setCall(snapshot);
        sub = followCheckIn(checkInId, snapshot.lastEventId, (events) => setCall((c) => (c ? applyEvents(c, events) : c)), setMode);
      },
      (e) => alive && setError(e),
    );
    return () => {
      alive = false;
      sub?.close();
    };
  }, [checkInId]);

  // Read receipts, browser notifications and the patient list react to changes.
  useEffect(() => {
    if (!call) return;
    const before = prev.current;
    prev.current = call;
    if (call.notes.length && (!before || before.notes.length !== call.notes.length || before.status !== call.status)) void clinicianApi.markCheckInRead(call.id).catch(() => undefined);
    if (!before) return;
    const newUrgent = call.notes.filter((n) => n.urgent && !before.notes.some((b) => b.id === n.id));
    if (newUrgent.length) {
      alertsChanged();
      if (notifyOn) notify("Urgent check-in note", `${patient.name}'s check-in has an urgent note in Context.`, `urgent-${call.id}`);
    }
    if (before.status !== "completed" && call.status === "completed") {
      onChanged();
      if (notifyOn && document.hidden) notify("Check-in finished", `${patient.name}'s check-in is ready in Context.`, `done-${call.id}`);
    }
  }, [call, notifyOn, onChanged, patient.name]);

  const close = useMemo(() => clinicCloseToday(clinic.openingHours, new Date(), zoneOffset(clinic.timeZone)), [clinic]);
  if (error) return <ErrorNote error={error} />;
  if (!call) return <Spinner label="Opening the check-in" />;

  const late = notReachedAlert({ status: call.status, linkedTiers: call.plan.linkedTiers, patientName: patient.name, clinicCloseAt: close, now: new Date() });
  const noteNumber = (id: string) => call.notes.findIndex((n) => n.id === id) + 1;
  const jump = (lineIds: string[]) => {
    const id = lineIds[0];
    if (!id) return;
    setLit(id);
    document.getElementById(`line-${call.id}-${id}`)?.scrollIntoView({ behavior: "smooth", block: "center" });
  };
  const acknowledge = async (noteId: string) => {
    try {
      const note = await clinicianApi.acknowledgeNote(call.id, noteId);
      setCall((c) => (c ? { ...c, notes: c.notes.map((n) => (n.id === note.id ? note : n)) } : c));
      alertsChanged();
    } catch (e) {
      setError(e);
    }
  };
  const addToCheck = async () => {
    setAdding(true);
    try {
      // One record from the call for the whole choice, as the backend's batch route makes it.
      const { notes } = await clinicianApi.addNotesToCheck(call.id, picked);
      const byId = new Map(notes.map((note) => [note.id, note]));
      setCall((c) => (c ? { ...c, notes: c.notes.map((n) => byId.get(n.id) ?? n) } : c));
      setAdded(picked.length);
      setPicked([]);
      onChanged();
    } catch (e) {
      setError(errorText(e));
    } finally {
      setAdding(false);
    }
  };
  const callButton = (className: string) =>
    patient.phone && (
      <Button asChild size="sm" className={className}>
        <a href={tel(patient.phone)}>
          <Icon name="phone-line" size={14} /> Call {first}
        </a>
      </Button>
    );

  return (
    <div className="flex min-w-0 flex-col gap-4">
      <div className="grid grid-cols-1 items-start gap-5 @min-[1100px]/pd:grid-cols-[minmax(0,1fr)_minmax(320px,400px)]">
        <section aria-label={`Check-in call with ${patient.name}`} className="flex min-w-0 flex-col gap-4 rounded-2xl bg-page p-5 shadow-card max-sm:p-4">
          <header className="flex flex-wrap items-center gap-x-3 gap-y-2">
            <img src="/assets/logo-mark.svg" alt="" width={28} height={28} />
            <h2 className="m-0 min-w-0 flex-1 text-[17px] leading-6 font-semibold tracking-[-0.01em] text-ink">Check-in call · {patient.name}</h2>
            <span role="status" className="inline-flex items-center gap-2">
              {isLive(call.status) && <span aria-hidden="true" className="size-2 animate-pulse rounded-full bg-green-icon motion-reduce:animate-none" />}
              <Pill pill={checkInPill(call.status)} />
              <small className="font-mono text-[12px] text-subtle">· {clock(call.statusAt)}</small>
            </span>
          </header>
          <p className="m-0 text-[13.5px] leading-5 text-cell">
            Yasmar asks, {first} answers. Notes appear here for {doctor}.
            <span className="text-muted"> {mode === "stream" ? "Live updates." : mode === "polling" ? "Updating every 3 seconds." : ""}</span>
          </p>
          <StatusTrail call={call} />
          {call.status === "later" && call.later && (
            <Callout tone="blue" icon="time-line">
              {first} chose a later time: {dayLabel(call.later.start).toLowerCase()} {clock(call.later.start)} to {clock(call.later.end)}.
            </Callout>
          )}
          {call.alerts.map((a) => (
            <Callout key={a.id} tone={a.kind === "not_reached" || a.kind === "escalated" ? "red" : "grey"} role="status">
              {a.message} <small className="font-mono text-[11.5px] opacity-70">· {clock(a.at)}</small>
            </Callout>
          ))}
          {late && <UrgentBox title={late} actions={callButton("bg-red-icon text-white hover:bg-red-text no-underline")} />}
          {call.notes
            .filter((n) => n.urgent)
            .map((n) => (
              <UrgentBox
                key={n.id}
                title={`Urgent: ${n.text}`}
                actions={
                  <>
                    {callButton(onRed)}
                    {!n.acknowledgedAt && (
                      <Button size="sm" variant="red" onClick={() => void acknowledge(n.id)}>
                        <Icon name="checkbox-circle-line" size={15} /> Acknowledge
                      </Button>
                    )}
                  </>
                }
              >
                <q>{n.quote}</q>
                <small className="text-[12.5px] leading-[18px]">
                  {clock(n.at)} · Yasmar gave {first} the emergency line.{" "}
                  {n.acknowledgedAt ? `Acknowledged by ${n.acknowledgedBy} at ${clock(n.acknowledgedAt)}.` : "Waiting for you to acknowledge."}
                </small>
              </UrgentBox>
            ))}
          <NotesList call={call} first={first} picked={picked} onPick={(id, on) => setPicked((p) => (on ? [...p, id] : p.filter((x) => x !== id)))} onJump={jump} />
          <SummaryCard call={call} doctor={doctor} noteNumber={noteNumber} />
          {isEnded(call.status) && call.notes.some((n) => !n.urgent) && (
            <div className="flex flex-wrap items-center justify-between gap-3 rounded-xl px-4 py-3 shadow-[inset_0_0_0_1px_var(--color-line)]">
              {added > 0 ? (
                <>
                  <span className="min-w-0 flex-1 basis-64 text-[13px] leading-[19px] text-green-text">
                    {added} {added === 1 ? "note" : "notes"} added to {first}'s records, with this call as the source. Notes never remove a recorded fact.
                  </span>
                  <Button variant="primary" onClick={() => navigate(tabPath(patient.id, "check"))}>
                    <Icon name="refresh-line" size={15} /> Run the check again
                  </Button>
                </>
              ) : (
                <>
                  <span className="min-w-0 flex-1 basis-64 text-[13px] leading-[19px] text-muted">Choose the notes that matter, then add them to the check. They go through review like any fact.</span>
                  <Button variant="primary" disabled={!picked.length || adding} onClick={() => void addToCheck()}>
                    {adding ? "Adding…" : `Add ${picked.length || ""} to check`.replace("  ", " ")}
                  </Button>
                </>
              )}
            </div>
          )}
          {notificationsSupported() && !notifyOn && (
            <p className="m-0 text-[12.5px] text-muted">
              <button
                className={cn(linkButtonClass, "inline-flex items-center gap-1.5 text-[12.5px] font-normal text-cell")}
                onClick={async () => {
                  const ok = await askForNotifications();
                  setNotifyOn(ok);
                  if (ok) void clinicianApi.updateMe({ notifications: { ...me.notifications, browser: true } }).catch(() => undefined);
                }}
              >
                <Icon name="notification-3-line" size={14} /> Tell me in this browser when a check-in finishes or has an urgent note
              </button>
            </p>
          )}
        </section>
        <CallText call={call} first={first} lit={lit} noteNumber={noteNumber} />
      </div>
      <p className="m-0 flex flex-wrap items-center gap-x-2 gap-y-1 text-[13px] text-muted">
        <Link to={tabPath(patient.id, "check_ins")} className="inline-flex items-center gap-1.5 font-medium text-ink no-underline hover:underline hover:decoration-line hover:underline-offset-4">
          <Icon name="arrow-left-line" size={14} /> All check-ins for {first}
        </Link>
        {["sent", "ringing", "later", "missed"].includes(call.status) && (
          <>
            <span aria-hidden="true">·</span>
            <button className={cn(linkButtonClass, "font-normal text-muted")} onClick={() => void clinicianApi.cancelCheckIn(call.id).then(setCall, setError)}>
              Cancel this check-in
            </button>
          </>
        )}
      </p>
    </div>
  );
}
