'use client';

import { useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { Clock, Video, UserCheck, UserMinus } from 'lucide-react';
import { GlassCard } from '@sehat/ui';
import { videoClient, VideoClientError } from '@/lib/video-client';
import { useRealtimeNotifications } from '@/lib/realtime';
import {
  humanizeSpecialty,
  type AppointmentStatus,
} from '@/lib/appointments-types';

/**
 * Pre-call lobby for an upcoming appointment.
 *
 * Three things on screen:
 * 1. Countdown to the scheduled start, ticking every second.
 * 2. Doctor-status badge that flips to "joined" the moment a realtime
 *    `peer_joined` event arrives for this appointment.
 * 3. Join-call button that's gated by two rules:
 *      - within 5 minutes of the scheduled start
 *      - appointment status can still enter a call
 *    The button shows a tooltip explaining why it's disabled.
 */

const JOIN_WINDOW_MS = 5 * 60 * 1000;
const JOINABLE_STATUSES = new Set<AppointmentStatus>([
  'pending',
  'allocated',
  'confirmed',
  'in_progress',
]);

export interface AppointmentLobbyProps {
  appointment: {
    id: string;
    scheduledAt: string;
    status: AppointmentStatus;
    specialtyName: string;
    doctorName?: string | null;
    doctorTitle?: string | null;
    doctorId?: string;
  };
}

export function AppointmentLobby({
  appointment,
}: AppointmentLobbyProps): React.ReactElement {
  const router = useRouter();
  const { lastEvent, eventsSince } = useRealtimeNotifications();

  const [now, setNow] = useState(() => Date.now());
  const [doctorJoined, setDoctorJoined] = useState(false);
  const [joining, setJoining] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const scheduledAtMs = useMemo(
    () => new Date(appointment.scheduledAt).getTime(),
    [appointment.scheduledAt],
  );

  // Tick every second for the countdown + window check.
  useEffect(() => {
    const id = window.setInterval(() => setNow(Date.now()), 1000);
    return () => window.clearInterval(id);
  }, []);

  // Hydrate doctorJoined from any peer_joined events that arrived before mount.
  useEffect(() => {
    const matches = eventsSince(
      (e) =>
        (e.metadata as { event?: unknown } | null)?.event === 'peer_joined' &&
        (e.metadata as { appointmentId?: unknown } | null)?.appointmentId ===
          appointment.id,
    );
    if (matches.length > 0) setDoctorJoined(true);
    // Only on mount — subsequent events come through lastEvent below.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [appointment.id]);

  // React to incoming events.
  useEffect(() => {
    if (!lastEvent) return;
    const meta = (lastEvent.metadata ?? {}) as {
      event?: unknown;
      appointmentId?: unknown;
    };
    if (meta.event === 'peer_joined' && meta.appointmentId === appointment.id) {
      setDoctorJoined(true);
    }
  }, [lastEvent, appointment.id]);

  const msUntilStart = scheduledAtMs - now;
  const withinWindow = msUntilStart <= JOIN_WINDOW_MS;
  const statusAllowsJoin = JOINABLE_STATUSES.has(appointment.status);
  const canJoin = withinWindow && statusAllowsJoin;

  const disabledReason = !canJoin
    ? !statusAllowsJoin
      ? `This appointment is ${appointment.status.replace(/_/g, ' ')} and cannot be joined.`
      : `The join button unlocks 5 minutes before the scheduled start (${formatRelative(msUntilStart)}).`
    : null;

  async function handleJoin(): Promise<void> {
    setError(null);
    setJoining(true);
    try {
      await videoClient.start(appointment.id);
      router.push(`/call/${appointment.id}`);
    } catch (e) {
      setError(
        e instanceof VideoClientError
          ? e.message
          : e instanceof Error
            ? e.message
            : 'Failed to start call',
      );
      setJoining(false);
    }
  }

  return (
    <GlassCard tone="light" padding="md" className="border-brand/30">
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <p className="text-xs font-semibold uppercase tracking-wide text-brand">
            Next consult
          </p>
          <h3 className="mt-1 text-base font-bold text-ink">
            {appointment.doctorName
              ? `${appointment.doctorTitle ?? 'Dr.'} ${appointment.doctorName}`
              : humanizeSpecialty(appointment.specialtyName) || 'Consultation'}
          </h3>
          <p className="mt-0.5 text-xs font-semibold text-brand">
            {humanizeSpecialty(appointment.specialtyName)}
          </p>
        </div>
        <DoctorStatusBadge joined={doctorJoined} />
      </div>

      <div className="mt-4 flex items-center gap-2 rounded-lg bg-brand/5 px-3 py-2">
        <Clock className="h-4 w-4 text-brand" />
        <span className="text-sm font-semibold text-ink">
          {msUntilStart > 0
            ? `Starts in ${formatCountdown(msUntilStart)}`
            : msUntilStart > -JOIN_WINDOW_MS
              ? 'Scheduled time reached'
              : `Was scheduled ${formatRelative(msUntilStart)}`}
        </span>
      </div>

      {error ? (
        <p className="mt-3 rounded-md border border-error/40 bg-error/10 px-3 py-2 text-xs text-error">
          {error}
        </p>
      ) : null}

      <div className="mt-4">
        <button
          type="button"
          onClick={() => void handleJoin()}
          disabled={!canJoin || joining}
          title={disabledReason ?? undefined}
          className="inline-flex w-full items-center justify-center gap-2 rounded-lg bg-gradient-hero px-4 py-2.5 text-sm font-semibold text-white shadow-brand-glow transition-opacity disabled:cursor-not-allowed disabled:opacity-50"
        >
          <Video className="h-4 w-4" />
          {joining ? 'Joining…' : 'Join call'}
        </button>
        {!canJoin && disabledReason ? (
          <p className="mt-2 text-center text-[11px] text-ink-muted">
            {disabledReason}
          </p>
        ) : null}
      </div>
    </GlassCard>
  );
}

function DoctorStatusBadge({
  joined,
}: {
  joined: boolean;
}): React.ReactElement {
  return joined ? (
    <span className="inline-flex flex-shrink-0 items-center gap-1.5 rounded-pill bg-green-100 px-2.5 py-1 text-xs font-bold text-green-700">
      <UserCheck className="h-3.5 w-3.5" />
      Doctor has joined
    </span>
  ) : (
    <span className="inline-flex flex-shrink-0 items-center gap-1.5 rounded-pill bg-accent/15 px-2.5 py-1 text-xs font-bold text-accent-dark">
      <UserMinus className="h-3.5 w-3.5" />
      Doctor not joined yet
    </span>
  );
}

/** Countdown like "12:34:56" or "12m 34s" for sub-hour windows. */
function formatCountdown(ms: number): string {
  const totalSec = Math.max(0, Math.floor(ms / 1000));
  const h = Math.floor(totalSec / 3600);
  const m = Math.floor((totalSec % 3600) / 60);
  const s = totalSec % 60;
  if (h > 0) {
    return `${h}h ${m.toString().padStart(2, '0')}m ${s.toString().padStart(2, '0')}s`;
  }
  if (m > 0) {
    return `${m}m ${s.toString().padStart(2, '0')}s`;
  }
  return `${s}s`;
}

/** Human-readable relative timestamp ("3m ago", "in 12m"). Signed. */
function formatRelative(ms: number): string {
  const abs = Math.abs(ms);
  const min = Math.floor(abs / 60000);
  const hr = Math.floor(min / 60);
  if (hr >= 1) {
    return ms >= 0 ? `in ${hr}h` : `${hr}h ago`;
  }
  if (min >= 1) {
    return ms >= 0 ? `in ${min}m` : `${min}m ago`;
  }
  return ms >= 0 ? 'in <1m' : '<1m ago';
}
