'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslations } from 'next-intl';
import { useRouter } from '@/i18n/navigation';
import { appointmentsApi } from '@/lib/api/appointments';
import { treatmentCatalogApi } from '@/lib/api/treatment-catalog';
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
import { useAuth } from '@/lib/hooks/useAuth';
import { usePatientSearchQuery } from '@/lib/hooks/usePatientSearchQuery';
import { canEditAppointments, hasPermission } from '@/components/shared/permissions';
import type { AppointmentColumnProvider, AppointmentRecord } from '@/types/appointment';
import type { Patient } from '@/types/patient';
import { PatientSummaryCard } from '@/components/ui/patient/PatientSummaryCard';
import { PatientSearchCombobox } from '@/components/ui/patient/PatientSearchCombobox';
import { AppointmentBookingModal } from '@/components/ui/appointments/AppointmentBookingModal';
import { AppointmentScheduleGrid } from '@/components/ui/appointments/AppointmentScheduleGrid';
import { AppointmentScheduleLegend } from '@/components/ui/appointments/AppointmentScheduleLegend';
import { ScheduleDayPicker } from '@/components/ui/shared/ScheduleDayPicker';
import { useToast } from '@/lib/hooks/useToast';
import type { AppointmentPurpose } from '@/types/appointment';
import { getUserFacingError } from '@/components/shared/formatApiError';
import { compareLocalDayStart, getLocalDayIsoRange, startOfLocalDay } from '@/components/appointments/appointmentTime';

export function AppointmentsPage() {
  const t = useTranslations('appointments');
  const tErrors = useTranslations('errors');
  const tCommon = useTranslations('common');
  const router = useRouter();
  const { currentOrganization } = useAuth();
  const [scheduleDate, setScheduleDate] = useState(() => startOfLocalDay(new Date()));

  const [providers, setProviders] = useState<AppointmentColumnProvider[]>([]);
  const [appointments, setAppointments] = useState<AppointmentRecord[]>([]);
  const [treatmentCatalog, setTreatmentCatalog] = useState<TreatmentCatalogEntry[]>([]);
  const [loadingSchedule, setLoadingSchedule] = useState(false);
  const toast = useToast();

  const { search, setSearch, patients, loading: loadingPatients } = usePatientSearchQuery();
  const [selectedPatient, setSelectedPatient] = useState<Patient | undefined>();

  const [bookingOpen, setBookingOpen] = useState(false);
  const [bookingStartMinute, setBookingStartMinute] = useState(9 * 60);
  const [bookingProviderId, setBookingProviderId] = useState<string | null>(null);
  const [bookingProviderName, setBookingProviderName] = useState('');
  const [editingAppointmentId, setEditingAppointmentId] = useState<string | null>(null);
  const [savingAppointment, setSavingAppointment] = useState(false);
  const [deletingAppointment, setDeletingAppointment] = useState(false);

  const canManageAppointments = canEditAppointments(currentOrganization);
  const canEditPatients = hasPermission(currentOrganization, 'TAB_PATIENTS_EDIT');

  const todayStart = useMemo(() => startOfLocalDay(new Date()), []);
  const isViewingPastDay = useMemo(
    () => compareLocalDayStart(scheduleDate, todayStart) < 0,
    [scheduleDate, todayStart],
  );
  const activeEditingAppointment = useMemo(
    () => appointments.find((a) => a.id === editingAppointmentId) ?? null,
    [appointments, editingAppointmentId],
  );

  const scheduleLoadGen = useRef(0);

  const sortedPatients = useMemo(
    () =>
      [...patients].sort((a, b) =>
        `${a.firstName} ${a.lastName}`.localeCompare(`${b.firstName} ${b.lastName}`),
      ),
    [patients],
  );

  const navigateToAddPatient = useCallback(() => {
    if (!canEditPatients) {
      return;
    }
    router.push('/patients?action=create');
  }, [canEditPatients, router]);

  const loadSchedule = useCallback(async () => {
    if (!currentOrganization?.id) {
      return;
    }
    const gen = ++scheduleLoadGen.current;
    setLoadingSchedule(true);
    toast.setError('');
    try {
      const range = getLocalDayIsoRange(scheduleDate);
      const [pRes, aRes] = await Promise.all([
        appointmentsApi.columnProviders(scheduleDate),
        appointmentsApi.list(range),
      ]);
      if (gen !== scheduleLoadGen.current) {
        return;
      }
      setProviders(pRes.data ?? []);
      setAppointments(aRes.data ?? []);
    } catch (err: unknown) {
      if (gen !== scheduleLoadGen.current) {
        return;
      }
      toast.showError(getUserFacingError(err, tErrors, t('errorLoadSchedule')));
    } finally {
      if (gen === scheduleLoadGen.current) {
        setLoadingSchedule(false);
      }
    }
  }, [currentOrganization?.id, scheduleDate, t, tErrors, toast]);

  useEffect(() => {
    void loadSchedule();
  }, [loadSchedule]);

  useEffect(() => {
    void treatmentCatalogApi
      .list('appointment')
      .then((r) => setTreatmentCatalog(r.data ?? []))
      .catch(() => {});
  }, []);

  function appointmentEditBlockReason(appointment: AppointmentRecord): string | null {
    if (!canManageAppointments) {
      return tCommon('readOnlyAccess');
    }
    if (isViewingPastDay && appointment.hasTreatment) {
      return t('infoEditBlockedHasTreatment');
    }
    return null;
  }

  function handleSlotClick(startMinute: number, providerUserId: string, providerName: string) {
    if (!canManageAppointments) {
      toast.showInfo(tCommon('readOnlyAccess'));
      return;
    }
    if (isViewingPastDay) {
      toast.showInfo(t('infoPastViewOnly'));
      return;
    }
    setBookingStartMinute(startMinute);
    setBookingProviderId(providerUserId);
    setBookingProviderName(providerName);
    setEditingAppointmentId(null);
    setBookingOpen(true);
  }

  function handleAppointmentClick(appointment: AppointmentRecord) {
    const blockReason = appointmentEditBlockReason(appointment);
    if (blockReason) {
      toast.showInfo(blockReason);
      return;
    }
    const provider = providers.find((p) => p.userId === appointment.providerUserId);
    const start = new Date(appointment.startAt);
    setBookingStartMinute(start.getHours() * 60 + start.getMinutes());
    setBookingProviderId(appointment.providerUserId);
    setBookingProviderName(provider?.name ?? bookingProviderName);
    setEditingAppointmentId(appointment.id);
    setBookingOpen(true);
  }

  function handleAppointmentOutsideHours(appointment: AppointmentRecord) {
    toast.showError(t('errorOutsideHours'));
  }

  async function handleSaveAppointment(payload: {
    patientId: string;
    providerUserId: string;
    startAt: string;
    endAt: string;
    purpose: AppointmentPurpose;
  }) {
    setSavingAppointment(true);
    toast.setError('');
    try {
      if (activeEditingAppointment) {
        await appointmentsApi.update(activeEditingAppointment.id, payload);
      } else {
        await appointmentsApi.create(payload);
      }
      setBookingOpen(false);
      setEditingAppointmentId(null);
      toast.showSuccess(activeEditingAppointment ? t('successUpdated') : t('successSaved'));
      await loadSchedule();
    } catch (err: unknown) {
      toast.showError(
        getUserFacingError(
          err,
          tErrors,
          activeEditingAppointment ? t('errorUpdate') : t('errorSave'),
        ),
      );
    } finally {
      setSavingAppointment(false);
    }
  }

  async function handleDeleteEditingAppointment() {
    if (!activeEditingAppointment) {
      return;
    }
    if (!window.confirm(t('confirmRemove'))) {
      return;
    }
    setDeletingAppointment(true);
    toast.setError('');
    try {
      await appointmentsApi.remove(activeEditingAppointment.id);
      setBookingOpen(false);
      setEditingAppointmentId(null);
      toast.showSuccess(t('successRemoved'));
      await loadSchedule();
    } catch (err: unknown) {
      toast.showError(getUserFacingError(err, tErrors, t('errorDelete')));
    } finally {
      setDeletingAppointment(false);
    }
  }

  return (
    <div className="space-y-6">
      <div className="grid grid-cols-1 gap-3 sm:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] sm:items-center">
        <div className="flex min-w-0 flex-col gap-1">
          <h1 className="text-xl sm:text-2xl font-semibold text-text-primary">{t('title')}</h1>
          <p className="text-sm text-text-secondary">{t('subtitle')}</p>
        </div>
        <div className="flex min-w-0 justify-center">
          <ScheduleDayPicker
            compact
            className="max-w-sm"
            value={scheduleDate}
            onChange={(d) => setScheduleDate(startOfLocalDay(d))}
          />
        </div>
        <div className="flex min-w-0 items-center justify-end">
          {loadingSchedule ? (
            <p className="text-sm text-text-muted">{t('loadingSchedule')}</p>
          ) : null}
        </div>
      </div>

      <div className="grid grid-cols-1 xl:grid-cols-3 gap-6">
        <div className="xl:col-span-1 space-y-4">
          <div className="surface-card p-4">
            <PatientSearchCombobox
              search={search}
              onSearchChange={setSearch}
              patients={sortedPatients}
              loading={loadingPatients}
              selectedPatient={selectedPatient}
              onSelectPatient={setSelectedPatient}
              canAddPatient={canEditPatients}
              onAddPatient={navigateToAddPatient}
              placeholder={t('searchPlaceholder')}
              idleHint={t('searchHint')}
              emptyResultsMessage={t('patientSearchEmpty')}
              noPermissionMessage={t('noPermissionAdd')}
            />
          </div>
          <PatientSummaryCard patient={selectedPatient} />
        </div>

        <div className="xl:col-span-2 space-y-4">
          <AppointmentScheduleLegend treatmentCatalog={treatmentCatalog} />

          <AppointmentScheduleGrid
            day={scheduleDate}
            providers={providers}
            appointments={appointments}
            treatmentCatalog={treatmentCatalog}
            canBook={canManageAppointments && !isViewingPastDay}
            appointmentEditBlockReason={appointmentEditBlockReason}
            slotBlockReason={
              !canManageAppointments
                ? tCommon('readOnlyAccess')
                : isViewingPastDay
                  ? t('infoPastViewOnly')
                  : null
            }
            onSlotClick={(startMinute, uid, name) => handleSlotClick(startMinute, uid, name)}
            onAppointmentClick={(apt) => handleAppointmentClick(apt)}
            onAppointmentOutsideHours={(apt) => handleAppointmentOutsideHours(apt)}
          />
        </div>
      </div>

      <AppointmentBookingModal
        open={bookingOpen}
        scheduleDate={scheduleDate}
        initialPatient={selectedPatient}
        onPatientChange={setSelectedPatient}
        canAddPatient={canEditPatients}
        onAddPatient={navigateToAddPatient}
        providerUserId={bookingProviderId}
        providerName={bookingProviderName}
        initialStartMinute={bookingStartMinute}
        treatmentCatalog={treatmentCatalog}
        editingAppointment={activeEditingAppointment}
        onClose={() => {
          setBookingOpen(false);
          setEditingAppointmentId(null);
        }}
        onSubmit={handleSaveAppointment}
        loading={savingAppointment}
        canDelete={
          canManageAppointments &&
          !!activeEditingAppointment &&
          !activeEditingAppointment.hasTreatment
        }
        onDelete={() => handleDeleteEditingAppointment()}
        deleting={deletingAppointment}
      />
    </div>
  );
}
