'use client';

import { useEffect, useRef, useState } from 'react';
import {
  ChevronDown,
  ChevronRight,
  CheckCircle2,
  AlertCircle,
  AlertTriangle,
  Cloud,
  CloudOff,
} from 'lucide-react';
import { DenseCard } from '@sehat/ui';
import { api, ApiClientError } from '@/lib/api-client';

const AUTOSAVE_DEBOUNCE_MS = 1500;

type SoapKey = 'subjective' | 'objective' | 'assessment' | 'plan';

const SECTIONS: Array<{ key: SoapKey; label: string; placeholder: string }> = [
  {
    key: 'subjective',
    label: 'Subjective',
    placeholder:
      'Chief complaint, history of present illness, patient-reported symptoms…',
  },
  {
    key: 'objective',
    label: 'Objective',
    placeholder:
      'Vitals, exam findings, lab results, observable signs…',
  },
  {
    key: 'assessment',
    label: 'Assessment',
    placeholder:
      'Working diagnosis, differential considerations, clinical impression…',
  },
  {
    key: 'plan',
    label: 'Plan',
    placeholder:
      'Treatment, prescriptions, follow-up, patient education…',
  },
];

interface SoapEditorProps {
  appointmentId: string;
  /** Optional initial values when continuing a draft. */
  initial?: Partial<Record<SoapKey, string>> & {
    followUpInWeeks?: number;
  };
  /** Appointment status drives gating: notes can only be authored once
   *  the consult has started. */
  appointmentStatus?: string;
}

type SaveState = 'idle' | 'saving' | 'saved' | 'error' | 'dirty';

/**
 * Four-section SOAP notes editor with debounced autosave + sign-off button.
 * Posts to `/api/appointments/[id]/notes` on every burst of changes.
 *
 * Layout: each section collapses individually so the doctor can focus on one
 * pane while keeping the others visible.
 */
export function SoapEditor({ appointmentId, initial, appointmentStatus }: SoapEditorProps) {
  const consultStarted =
    appointmentStatus === 'in_progress' || appointmentStatus === 'completed';

  const [values, setValues] = useState<Record<SoapKey, string>>({
    subjective: initial?.subjective ?? '',
    objective: initial?.objective ?? '',
    assessment: initial?.assessment ?? '',
    plan: initial?.plan ?? '',
  });
  const [followUp, setFollowUp] = useState<number | undefined>(
    initial?.followUpInWeeks,
  );
  const [collapsed, setCollapsed] = useState<Record<SoapKey, boolean>>({
    subjective: false,
    objective: false,
    assessment: false,
    plan: false,
  });
  const [saveState, setSaveState] = useState<SaveState>('idle');
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [signedAt, setSignedAt] = useState<Date | null>(null);

  // Skip the autosave on first render (we don't want to push an empty draft
  // immediately on page load).
  const isMounted = useRef(false);

  useEffect(() => {
    if (!isMounted.current) {
      isMounted.current = true;
      return;
    }
    // Don't autosave until the consult has started — the backend will reject
    // it and the UI would just churn through errors.
    if (!consultStarted) {
      setSaveState('idle');
      return;
    }
    setSaveState('dirty');
    const t = window.setTimeout(() => {
      void autosave();
    }, AUTOSAVE_DEBOUNCE_MS);
    return () => window.clearTimeout(t);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [values.subjective, values.objective, values.assessment, values.plan, followUp, consultStarted]);

  async function autosave() {
    // Nothing meaningful to save yet.
    const hasContent =
      values.subjective.trim() ||
      values.objective.trim() ||
      values.assessment.trim() ||
      values.plan.trim();
    if (!hasContent) {
      setSaveState('idle');
      return;
    }
    setSaveState('saving');
    setErrorMessage(null);
    try {
      await api.saveAppointmentNotes(appointmentId, {
        subjective: values.subjective.trim() || undefined,
        objective: values.objective.trim() || undefined,
        assessment: values.assessment.trim() || undefined,
        plan: values.plan.trim() || undefined,
        followUpInWeeks: followUp,
      });
      setSaveState('saved');
    } catch (e) {
      setErrorMessage(
        e instanceof ApiClientError ? e.message : 'Autosave failed',
      );
      setSaveState('error');
    }
  }

  async function signOff() {
    await autosave();
    // We persist sign-off marker client-side only; the canonical signing
    // happens via the prescription endpoint. The notes endpoint does not
    // currently expose a separate "sign" route.
    setSignedAt(new Date());
  }

  const update = (key: SoapKey, value: string) =>
    setValues((v) => ({ ...v, [key]: value }));

  const toggle = (key: SoapKey) =>
    setCollapsed((c) => ({ ...c, [key]: !c[key] }));

  return (
    <DenseCard
      title="SOAP notes"
      actions={<AutosaveBadge state={saveState} message={errorMessage} />}
    >
      {!consultStarted ? (
        <div className="mb-3 flex items-start gap-2 rounded-md border border-warning-500/40 bg-warning-500/10 px-3 py-2 text-xs text-warning-500">
          <AlertTriangle className="mt-0.5 h-3.5 w-3.5 flex-shrink-0" />
          <div>
            <p className="font-semibold">Locked until the consult starts</p>
            <p className="opacity-90">
              Consultation notes can only be added once the video call has
              begun. Tap “Join call” to start.
            </p>
          </div>
        </div>
      ) : null}

      <div className={`space-y-3 ${consultStarted ? '' : 'pointer-events-none opacity-60'}`}>
        {SECTIONS.map((s) => (
          <div
            key={s.key}
            className="rounded-md border border-denseSurface-border bg-white"
          >
            <button
              type="button"
              onClick={() => toggle(s.key)}
              className="flex w-full items-center justify-between gap-2 px-3 py-2 text-left transition-colors hover:bg-denseSurface-bg"
              aria-expanded={!collapsed[s.key]}
            >
              <span className="flex items-center gap-1.5">
                {collapsed[s.key] ? (
                  <ChevronRight className="h-3.5 w-3.5 text-ink-3" />
                ) : (
                  <ChevronDown className="h-3.5 w-3.5 text-ink-3" />
                )}
                <span className="text-sm font-semibold text-ink-1">
                  {s.label}
                </span>
                {values[s.key].trim() ? (
                  <span className="rounded bg-brand-50 px-1.5 py-0.5 font-mono text-[10px] font-semibold text-brand-700">
                    {values[s.key].trim().split(/\s+/).length} w
                  </span>
                ) : null}
              </span>
            </button>
            {!collapsed[s.key] ? (
              <div className="border-t border-denseSurface-border p-2">
                <textarea
                  value={values[s.key]}
                  onChange={(e) => update(s.key, e.target.value)}
                  placeholder={s.placeholder}
                  rows={3}
                  className="w-full resize-y rounded-md border border-denseSurface-border bg-white px-2.5 py-2 text-sm leading-relaxed text-ink-1 focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/30"
                />
              </div>
            ) : null}
          </div>
        ))}

        <div className="flex flex-wrap items-end gap-3 border-t border-denseSurface-border pt-3">
          <div>
            <label
              htmlFor="soap-followup"
              className="block text-[11px] font-semibold uppercase tracking-wide text-ink-2"
            >
              Follow-up (weeks)
            </label>
            <input
              id="soap-followup"
              type="number"
              min={0}
              value={followUp ?? ''}
              onChange={(e) =>
                setFollowUp(
                  e.target.value === '' ? undefined : Number(e.target.value),
                )
              }
              className="mt-1 h-9 w-28 rounded-md border border-denseSurface-border bg-white px-2 font-mono text-sm text-ink-1 focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/30"
            />
          </div>
          <div className="ml-auto flex items-center gap-2">
            {signedAt ? (
              <span className="inline-flex items-center gap-1 text-xs font-semibold text-success-500">
                <CheckCircle2 className="h-4 w-4" />
                Signed {signedAt.toLocaleTimeString(undefined, {
                  hour: 'numeric',
                  minute: '2-digit',
                })}
              </span>
            ) : (
              <button
                type="button"
                onClick={() => void signOff()}
                className="inline-flex items-center gap-1.5 rounded-md bg-brand-500 px-3 py-1.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-brand-600"
              >
                <CheckCircle2 className="h-4 w-4" />
                Sign &amp; save
              </button>
            )}
          </div>
        </div>
      </div>
    </DenseCard>
  );
}

function AutosaveBadge({
  state,
  message,
}: {
  state: SaveState;
  message: string | null;
}) {
  if (state === 'idle') {
    return (
      <span className="inline-flex items-center gap-1 text-[11px] text-ink-3">
        <Cloud className="h-3.5 w-3.5" />
        Autosave on
      </span>
    );
  }
  if (state === 'dirty' || state === 'saving') {
    return (
      <span className="inline-flex items-center gap-1 text-[11px] text-ink-2">
        <Cloud className="h-3.5 w-3.5 animate-pulse" />
        Saving…
      </span>
    );
  }
  if (state === 'saved') {
    return (
      <span className="inline-flex items-center gap-1 text-[11px] font-semibold text-success-500">
        <CheckCircle2 className="h-3.5 w-3.5" />
        Saved
      </span>
    );
  }
  // error
  return (
    <span
      className="inline-flex items-center gap-1 text-[11px] font-semibold text-danger-500"
      title={message ?? undefined}
    >
      <CloudOff className="h-3.5 w-3.5" />
      <AlertCircle className="h-3 w-3" /> Save failed
    </span>
  );
}
