'use client';

import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { Clock, UserCheck, UserX, Video, Loader2 } from 'lucide-react';
import { useRealtimeNotifications } from '@/lib/realtime';

const JOIN_WINDOW_MS = 5 * 60 * 1_000; // 5 minutes before scheduled start

/**
 * URL the doctor is sent to when they click "Start consult". The actual live
 * call UI lives in a separate app (currently the patient portal on :3001 — see
 * existing usages in `app/appointments/page.tsx`). When a dedicated doctor
 * call route is built, swap this constant out.
 */
const CALL_BASE_URL =
  process.env.NEXT_PUBLIC_CALL_BASE_URL ?? '/call';

interface AppointmentLobbyProps {
  appointment: {
    id: string;
    scheduledAt: string;
    status: string;
    specialtyName: string;
    patientName?: string | null;
    patientId?: string | null;
  };
}

interface Countdown {
  label: string;
  /** ms until scheduled start; negative if overdue. */
  diffMs: number;
}

function formatCountdown(diffMs: number): string {
  const abs = Math.abs(diffMs);
  const totalSeconds = Math.floor(abs / 1_000);
  const hours = Math.floor(totalSeconds / 3_600);
  const minutes = Math.floor((totalSeconds % 3_600) / 60);
  const seconds = totalSeconds % 60;
  const pad = (n: number): string => n.toString().padStart(2, '0');
  if (hours > 0) return `${hours}:${pad(minutes)}:${pad(seconds)}`;
  return `${pad(minutes)}:${pad(seconds)}`;
}

/**
 * Pre-call lobby panel for the doctor's appointment detail page. Shows:
 *
 *  • A live countdown to scheduled start (counts up after the time passes).
 *  • A "patient status" badge that flips green once a `peer_joined` realtime
 *    event is received. The flag is sticky — once joined, stays joined.
 *  • A gated "Start consult" button. Enabled only when (a) within 5 min of
 *    the scheduled start AND (b) appointment status can still enter a call.
 *    Otherwise disabled with an explanatory tooltip.
 *
 * On click, posts to the backend's call-start endpoint then navigates to the
 * external call UI. We don't block navigation if call-start fails — failure
 * just gets surfaced inline so the doctor can retry.
 */
export function AppointmentLobby({ appointment }: AppointmentLobbyProps) {
  const router = useRouter();
  const scheduledMs = useMemo(
    () => new Date(appointment.scheduledAt).getTime(),
    [appointment.scheduledAt],
  );

  // Tick every second for the countdown.
  const [now, setNow] = useState<number>(() => Date.now());
  useEffect(() => {
    const id = window.setInterval(() => setNow(Date.now()), 1_000);
    return () => window.clearInterval(id);
  }, []);

  const countdown = useMemo<Countdown>(() => {
    const diffMs = scheduledMs - now;
    if (diffMs > 0) return { label: `Starts in ${formatCountdown(diffMs)}`, diffMs };
    return { label: `Overdue by ${formatCountdown(diffMs)}`, diffMs };
  }, [scheduledMs, now]);

  // Patient-joined sticky flag, driven by the realtime stream.
  const [patientJoined, setPatientJoined] = useState<boolean>(false);
  useRealtimeNotifications({
    onEvent: (n) => {
      const meta = n.metadata as { event?: string; appointmentId?: string } | null;
      if (!meta) return;
      if (meta.event !== 'peer_joined') return;
      // If the event carries an appointmentId, scope to this appointment.
      if (meta.appointmentId && meta.appointmentId !== appointment.id) return;
      setPatientJoined(true);
    },
  });

  // Gating logic for the Start button.
  const withinWindow = countdown.diffMs <= JOIN_WINDOW_MS; // 5 min before -> any time after
  const statusOk =
    appointment.status === 'pending' ||
    appointment.status === 'allocated' ||
    appointment.status === 'confirmed' ||
    appointment.status === 'in_progress';
  const canStart = withinWindow && statusOk;
  const disabledReason = !statusOk
    ? `This appointment is ${appointment.status.replace(/_/g, ' ')} and cannot be started.`
    : !withinWindow
      ? `You can start the consult up to 5 minutes before the scheduled time.`
      : null;

  const [starting, setStarting] = useState<boolean>(false);
  const [startError, setStartError] = useState<string | null>(null);

  const handleStart = useCallback(async () => {
    if (!canStart || starting) return;
    setStarting(true);
    setStartError(null);
    try {
      const res = await fetch(`/api/appointments/${appointment.id}/call/start`, {
        method: 'POST',
      });
      if (!res.ok) {
        const data = (await res.json().catch(() => null)) as {
          message?: string | string[];
        } | null;
        const msg = Array.isArray(data?.message)
          ? data?.message.join('\n')
          : data?.message ?? `Failed to start call (${res.status})`;
        setStartError(msg);
        return;
      }
      // TODO: replace with a real doctor-portal call route once it exists.
      // For now we send the doctor to the same call URL the rest of the app uses.
      const url = `${CALL_BASE_URL}/${appointment.id}`;
      if (typeof window !== 'undefined') {
        window.location.href = url;
      } else {
        router.push(url);
      }
    } catch (e) {
      setStartError(e instanceof Error ? e.message : 'Network error');
    } finally {
      setStarting(false);
    }
  }, [appointment.id, canStart, starting, router]);

  const overdue = countdown.diffMs < 0;

  return (
    <section
      aria-label="Pre-call lobby"
      className="rounded-lg border border-denseSurface-border bg-white p-4 shadow-sm"
    >
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div className="min-w-0">
          <h2 className="font-display text-sm font-bold uppercase tracking-wide text-ink-3">
            Pre-call lobby
          </h2>
          <p className="mt-1 text-base font-semibold text-ink-1">
            {appointment.patientName ?? 'Patient'}
            {appointment.specialtyName ? (
              <span className="ml-2 text-sm font-normal text-ink-2">
                · {appointment.specialtyName}
              </span>
            ) : null}
          </p>
        </div>

        <PatientStatusBadge joined={patientJoined} />
      </div>

      <div className="mt-3 flex flex-wrap items-center gap-3">
        <span
          className={`inline-flex items-center gap-1.5 rounded-md px-2 py-1 font-mono text-sm font-semibold ${
            overdue
              ? 'bg-warning-500/10 text-warning-500'
              : 'bg-denseSurface-bg text-ink-1'
          }`}
        >
          <Clock className="h-4 w-4" aria-hidden="true" />
          {countdown.label}
        </span>
      </div>

      <div className="mt-4">
        <button
          type="button"
          onClick={handleStart}
          disabled={!canStart || starting}
          title={disabledReason ?? 'Start the consultation'}
          aria-disabled={!canStart || starting}
          className={`inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-semibold shadow-sm transition-colors ${
            canStart && !starting
              ? 'bg-brand-500 text-white hover:bg-brand-600'
              : 'cursor-not-allowed bg-denseSurface-bg text-ink-3'
          }`}
        >
          {starting ? (
            <Loader2 className="h-4 w-4 animate-spin" />
          ) : (
            <Video className="h-4 w-4" />
          )}
          {starting ? 'Starting…' : 'Start consult'}
        </button>
        {disabledReason ? (
          <p className="mt-2 text-xs text-ink-3">{disabledReason}</p>
        ) : null}
        {startError ? (
          <p className="mt-2 text-xs text-danger-500">{startError}</p>
        ) : null}
      </div>
    </section>
  );
}

function PatientStatusBadge({ joined }: { joined: boolean }) {
  if (joined) {
    return (
      <span className="inline-flex items-center gap-1.5 rounded-full border border-success-500/30 bg-success-500/10 px-2.5 py-1 text-xs font-semibold text-success-500">
        <UserCheck className="h-3.5 w-3.5" />
        Patient is waiting
      </span>
    );
  }
  return (
    <span className="inline-flex items-center gap-1.5 rounded-full border border-warning-500/30 bg-warning-500/10 px-2.5 py-1 text-xs font-semibold text-warning-500">
      <UserX className="h-3.5 w-3.5" />
      Patient not joined yet
    </span>
  );
}
