'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslations } from 'next-intl';
import { AppointmentsStrip } from '@/components/ui/treatment/AppointmentsStrip';
import { FdiToothChart } from '@/components/ui/treatment/FdiToothChart';
import { PastTreatmentsPanel } from '@/components/ui/treatment/PastTreatmentsPanel';
import { TreatmentCasesEditor } from '@/components/ui/treatment/TreatmentCasesEditor';
import { TreatmentPreviewCard } from '@/components/ui/treatment/TreatmentPreviewCard';
import {
  TreatmentPreviewDialog,
  type TreatmentPreviewMode,
} from '@/components/ui/treatment/TreatmentPreviewDialog';
import { ToastStack } from '@/components/ui/shared/Toast';
import {
  addCalendarDays,
  compareLocalDayStart,
  isSameLocalCalendarDay,
  startOfLocalDay,
} from '@/components/appointments/appointmentTime';
import { appointmentsApi } from '@/lib/api/appointments';
import { treatmentsApi } from '@/lib/api/treatments';
import { pickAutoAppointment } from '@/components/shared/treatmentSelection';
import { canEditTreatment, canViewTreatment } from '@/components/shared/permissions';
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
import { useToast } from '@/lib/hooks/useToast';
import type { Organization } from '@/types/organization';
import type { AppointmentRecord } from '@/types/appointment';
import type {
  FdiToothId,
  LinkedOrganizationOption,
  PastTreatment,
  PastTreatmentCase,
  TreatmentAppointment,
  TreatmentCaseDraft,
} from '@/types/treatment';

const TREATMENT_TYPE_KEYS = {
  consultation: 'typeConsultation',
  filling: 'typeFilling',
  endo: 'typeEndo',
  visit: 'typeVisit',
  hygiene: 'typeHygiene',
} as const;

function newCase(): TreatmentCaseDraft {
  return {
    clientId:
      typeof crypto !== 'undefined' && 'randomUUID' in crypto
        ? crypto.randomUUID()
        : `case-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
    treatmentType: 'consultation',
    teeth: [],
    comment: '',
    attachmentMetas: [],
    sendToOrganizationIds: [],
    sentAt: null,
  };
}

function mapAppointment(record: AppointmentRecord): TreatmentAppointment {
  return {
    id: record.id,
    patientId: record.patientId,
    patientFirstName: record.patient.firstName,
    patientLastName: record.patient.lastName,
    providerUserId: record.providerUserId,
    startAt: record.startAt,
    endAt: record.endAt,
    purpose: record.purpose,
  };
}

function mapCaseFromApi(c: PastTreatmentCase): TreatmentCaseDraft {
  return {
    clientId: c.clientId,
    id: c.id,
    treatmentType: c.treatmentType,
    teeth: c.teeth,
    comment: c.notes ?? '',
    attachmentMetas: c.attachmentMetas ?? [],
    sendToOrganizationIds: c.sendToOrganizationIds ?? [],
    sends: c.sends ?? [],
    sentAt: c.sentAt ?? null,
  };
}

function serializeCases(cases: TreatmentCaseDraft[]) {
  return JSON.stringify(
    cases.map((c) => ({
      clientId: c.clientId,
      id: c.id,
      treatmentType: c.treatmentType,
      teeth: c.teeth,
      comment: c.comment,
      attachmentMetas: c.attachmentMetas,
      sendToOrganizationIds: c.sendToOrganizationIds,
      sentAt: c.sentAt,
    })),
  );
}

function casesToPreviewTreatment(
  cases: TreatmentCaseDraft[],
  meta: { title: string; patientId: string; treatmentAt: string; status: string; id?: string },
): PastTreatment {
  return {
    id: meta.id ?? 'current-draft',
    patientId: meta.patientId,
    title: meta.title,
    treatmentAt: meta.treatmentAt,
    status: meta.status,
    cases: cases.map((c, idx) => ({
      id: c.id ?? c.clientId ?? `draft-${idx + 1}`,
      clientId: c.clientId,
      treatmentType: c.treatmentType,
      teeth: c.teeth,
      notes: c.comment || null,
      attachmentMetas: c.attachmentMetas,
      sendToOrganizationIds: c.sendToOrganizationIds,
      sentAt: c.sentAt ?? null,
    })),
    documents: [],
  };
}

interface TreatmentWorkspaceProps {
  userId: string;
  currentOrganization: Organization | null;
}

export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWorkspaceProps) {
  const t = useTranslations('treatment');
  const { showError, showSuccess, messages: toastMessages } = useToast();
  const canView = canViewTreatment(currentOrganization);
  const canEdit = canEditTreatment(currentOrganization);

  const [stripHidden, setStripHidden] = useState(false);
  const todayStart = useMemo(() => startOfLocalDay(new Date()), []);

  const [selectedDay, setSelectedDay] = useState(() => startOfLocalDay(new Date()));
  const [appointments, setAppointments] = useState<TreatmentAppointment[]>([]);
  const [apptsLoading, setApptsLoading] = useState(false);
  const [selectionLocked, setSelectionLocked] = useState(false);
  const [selectedAppointmentId, setSelectedAppointmentId] = useState<string | null>(null);

  const [history, setHistory] = useState<PastTreatment[]>([]);
  const [historyLoading, setHistoryLoading] = useState(false);

  const [orgs, setOrgs] = useState<LinkedOrganizationOption[]>([]);

  const [cases, setCases] = useState<TreatmentCaseDraft[]>(() => [newCase()]);
  const [activeCaseId, setActiveCaseId] = useState<string>(() => cases[0].clientId);
  const [savedSnapshot, setSavedSnapshot] = useState<string | null>(null);

  const selectionLockedRef = useRef(selectionLocked);
  selectionLockedRef.current = selectionLocked;

  const [saveBusy, setSaveBusy] = useState(false);
  const [sendBusyId, setSendBusyId] = useState<string | null>(null);
  const [uploadBusyCaseId, setUploadBusyCaseId] = useState<string | null>(null);
  const [organizationSearch, setOrganizationSearch] = useState('');
  const [recentOrganizationIds, setRecentOrganizationIds] = useState<string[]>([]);

  const [previewOpen, setPreviewOpen] = useState(false);
  const [previewTreatment, setPreviewTreatment] = useState<PastTreatment | null>(null);
  const [previewMode, setPreviewMode] = useState<TreatmentPreviewMode>('readonly');

  const isDirty = useMemo(() => {
    if (savedSnapshot === null) {
      return cases.length !== 1 || cases[0].comment !== '' || cases[0].teeth.length > 0;
    }
    return serializeCases(cases) !== savedSnapshot;
  }, [cases, savedSnapshot]);

  const selectedAppointment = useMemo(
    () => appointments.find((a) => a.id === selectedAppointmentId) ?? null,
    [appointments, selectedAppointmentId],
  );

  const isViewingPastDay = useMemo(
    () => compareLocalDayStart(selectedDay, todayStart) < 0,
    [selectedDay, todayStart],
  );

  const canEditTreatmentForDay = canEdit && Boolean(selectedAppointment) && !isViewingPastDay;

  const activeCase = useMemo(
    () => cases.find((c) => c.clientId === activeCaseId) ?? cases[0],
    [cases, activeCaseId],
  );

  const selectedTeethSet = useMemo(() => new Set(activeCase?.teeth ?? []), [activeCase?.teeth]);

  const currentDraftPreview = useMemo<PastTreatment | null>(() => {
    if (!selectedAppointment) return null;
    return casesToPreviewTreatment(cases, {
      title: t('draftTitle', {
        patientName: `${selectedAppointment.patientFirstName} ${selectedAppointment.patientLastName}`,
      }),
      patientId: selectedAppointment.patientId,
      treatmentAt: new Date().toISOString(),
      status: 'draft',
    });
  }, [cases, selectedAppointment, t]);

  useEffect(() => {
    setSelectionLocked(false);
  }, [selectedDay]);

  useEffect(() => {
    let cancelled = false;
    setApptsLoading(true);
    void (async () => {
      try {
        const dayStart = startOfLocalDay(selectedDay);
        const dayEnd = addCalendarDays(dayStart, 1);
        const response = await appointmentsApi.list({
          from: dayStart.toISOString(),
          to: dayEnd.toISOString(),
        });
        if (cancelled) return;
        const list = response.data
          .filter((a) => a.providerUserId === userId)
          .map(mapAppointment);
        setAppointments(list);
        if (!selectionLockedRef.current) {
          setSelectedAppointmentId(pickAutoAppointment(list, selectedDay));
        }
      } catch (error: unknown) {
        if (!cancelled) {
          showError(formatApiErrorMessage(error, t('errorLoadAppointments')));
        }
      } finally {
        if (!cancelled) setApptsLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [userId, selectedDay, showError, t]);

  useEffect(() => {
    const today = startOfLocalDay(new Date());
    if (!isSameLocalCalendarDay(selectedDay, today) || selectionLocked) return;

    const id = window.setInterval(() => {
      setSelectedAppointmentId((prev) => {
        const next = pickAutoAppointment(appointments, selectedDay);
        return next ?? prev;
      });
    }, 60_000);

    return () => window.clearInterval(id);
  }, [selectedDay, appointments, selectionLocked]);

  useEffect(() => {
    let cancelled = false;
    void (async () => {
      try {
        const list = await treatmentsApi.listLinkedOrganizations();
        if (!cancelled) setOrgs(list.data);
      } catch (error: unknown) {
        if (!cancelled) {
          showError(formatApiErrorMessage(error, t('errorLoadOrgs')));
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [showError, t]);

  useEffect(() => {
    if (!selectedAppointment) {
      setHistory([]);
      return;
    }
    let cancelled = false;
    setHistoryLoading(true);
    void (async () => {
      try {
        const response = await treatmentsApi.listPatientHistory(selectedAppointment.patientId);
        if (!cancelled) setHistory(response.data);
      } catch (error: unknown) {
        if (!cancelled) {
          showError(formatApiErrorMessage(error, t('errorLoadHistory')));
        }
      } finally {
        if (!cancelled) setHistoryLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [selectedAppointment?.patientId, showError, t]);

  useEffect(() => {
    const appointmentId = selectedAppointment?.id;
    if (!appointmentId) return;

    let cancelled = false;
    void (async () => {
      try {
        const response = await treatmentsApi.getDraft(appointmentId);
        if (cancelled) return;

        if (response.data?.cases?.length) {
          const mapped = response.data.cases.map(mapCaseFromApi);
          setCases(mapped);
          setActiveCaseId((prev) => {
            const stillExists = mapped.some((c) => c.clientId === prev);
            return stillExists ? prev : mapped[0].clientId;
          });
          setSavedSnapshot(serializeCases(mapped));
        } else {
          const first = newCase();
          setCases([first]);
          setActiveCaseId(first.clientId);
          setSavedSnapshot(serializeCases([first]));
        }
        setOrganizationSearch('');
      } catch (error: unknown) {
        if (!cancelled) {
          showError(formatApiErrorMessage(error, t('errorLoadDraft')));
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [selectedAppointment?.id, showError, t]);

  const confirmDiscardIfDirty = useCallback(() => {
    if (!isDirty) return true;
    return window.confirm(t('confirmDiscard'));
  }, [isDirty, t]);

  const onPickAppointment = useCallback(
    (id: string) => {
      if (!confirmDiscardIfDirty()) return;
      setSelectionLocked(true);
      setSelectedAppointmentId(id);
    },
    [confirmDiscardIfDirty],
  );

  const onSelectDay = useCallback(
    (day: Date) => {
      if (!confirmDiscardIfDirty()) return;
      setSelectedDay(day);
    },
    [confirmDiscardIfDirty],
  );

  const uploadForCase = useCallback(
    async (caseClientId: string, files: FileList | File[]) => {
      if (!canEditTreatmentForDay || !selectedAppointment) return;
      const list = files instanceof FileList ? Array.from(files) : files;
      if (!list.length) return;

      setUploadBusyCaseId(caseClientId);
      try {
        const uploaded = await treatmentsApi.uploadCaseAttachments(
          selectedAppointment.id,
          caseClientId,
          list,
        );
        setCases((prev) =>
          prev.map((c) =>
            c.clientId === caseClientId
              ? { ...c, attachmentMetas: [...c.attachmentMetas, ...uploaded.data] }
              : c,
          ),
        );
        showSuccess(t('successFilesUploaded', { count: uploaded.data.length }));
      } catch (error: unknown) {
        showError(formatApiErrorMessage(error, t('errorUpload')));
      } finally {
        setUploadBusyCaseId(null);
      }
    },
    [canEditTreatmentForDay, selectedAppointment, showSuccess, showError, t],
  );

  const persistDraft = useCallback(async () => {
    if (!selectedAppointment) throw new Error('No appointment selected');

    const response = await treatmentsApi.saveDraft(selectedAppointment.id, {
      cases: cases.map(({ clientId, id, treatmentType, teeth, comment, attachmentMetas }) => ({
        clientId,
        id,
        treatmentType,
        teeth,
        comment,
        attachmentIds: attachmentMetas.map((a) => a.id),
      })),
    });
    const mapped = response.data.cases.map(mapCaseFromApi);
    setCases(mapped);
    setActiveCaseId((prev) => {
      const stillExists = mapped.some((c) => c.clientId === prev);
      return stillExists ? prev : mapped[0]?.clientId ?? prev;
    });
    setSavedSnapshot(serializeCases(mapped));
    return response.data;
  }, [cases, selectedAppointment]);

  const handleSaveAll = useCallback(async () => {
    if (!canEditTreatmentForDay || !selectedAppointment) return;
    setSaveBusy(true);
    try {
      await persistDraft();
      showSuccess(t('successDraftSaved'));
    } catch (error: unknown) {
      showError(formatApiErrorMessage(error, t('errorSaveDraft')));
    } finally {
      setSaveBusy(false);
    }
  }, [canEditTreatmentForDay, selectedAppointment, persistDraft, showSuccess, showError, t]);

  const handleSendCase = useCallback(
    async (treatmentCase: TreatmentCaseDraft) => {
      if (!canEditTreatmentForDay || !selectedAppointment) return;
      const targets = treatmentCase.sendToOrganizationIds.filter((id) =>
        orgs.some((o) => o.id === id && o.active),
      );
      if (targets.length === 0) {
        showError(t('errorChooseOrg'));
        return;
      }
      setSendBusyId(treatmentCase.clientId);
      try {
        const saved = await persistDraft();
        const serverCase = saved.cases.find((c) => c.clientId === treatmentCase.clientId);
        if (!serverCase?.id) throw new Error(t('errorCaseMustSave'));

        const response = await treatmentsApi.sendCase(serverCase.id, { organizationIds: targets });
        setCases((prev) => {
          const next = prev.map((c) =>
            c.clientId === treatmentCase.clientId
              ? {
                  ...c,
                  id: response.data.id,
                  sentAt: response.data.sentAt,
                  sendToOrganizationIds: response.data.sendToOrganizationIds,
                  sends: response.data.sends,
                }
              : c,
          );
          setSavedSnapshot(serializeCases(next));
          return next;
        });
        setRecentOrganizationIds((prev) => {
          const next = [...targets.filter((id) => !prev.includes(id)), ...prev];
          return next.slice(0, 10);
        });
        showSuccess(t('successCaseSent'));
      } catch (error: unknown) {
        showError(formatApiErrorMessage(error, t('errorSendCase')));
      } finally {
        setSendBusyId(null);
      }
    },
    [canEditTreatmentForDay, selectedAppointment, orgs, persistDraft, showSuccess, showError, t],
  );

  const openPreview = useCallback(
    (treatment: PastTreatment, mode: TreatmentPreviewMode) => {
      setPreviewTreatment(treatment);
      setPreviewMode(mode);
      setPreviewOpen(true);
    },
    [],
  );

  const openCurrentDraftPreview = useCallback(() => {
    if (!currentDraftPreview) return;
    openPreview(currentDraftPreview, canEditTreatmentForDay ? 'editable' : 'readonly');
  }, [currentDraftPreview, canEditTreatmentForDay, openPreview]);

  const getCaseOrgIds = useCallback(
    (caseKey: string) => cases.find((c) => c.clientId === caseKey)?.sendToOrganizationIds ?? [],
    [cases],
  );

  const toggleCaseOrg = useCallback((caseKey: string, orgId: string, checked: boolean) => {
    setCases((prev) =>
      prev.map((c) => {
        if (c.clientId !== caseKey || c.sentAt) return c;
        const next = new Set(c.sendToOrganizationIds);
        if (checked) next.add(orgId);
        else next.delete(orgId);
        return { ...c, sendToOrganizationIds: [...next] };
      }),
    );
  }, []);

  if (!canView) {
    return (
      <div className="surface-card p-6 max-w-xl">
        <h2 className="text-lg font-semibold text-text-primary">{t('noPermissionTitle')}</h2>
        <p className="text-sm text-text-secondary mt-2">
          {t('noPermissionBody')}
        </p>
      </div>
    );
  }

  return (
    <div className="space-y-4">
      <header className="space-y-1">
        <h1 className="text-2xl font-semibold text-text-primary">{t('title')}</h1>
        <p className="text-sm text-text-secondary">
          {canEdit ? t('subtitleEdit') : t('subtitleReadOnly')}
        </p>
      </header>

      <ToastStack {...toastMessages} />

      <AppointmentsStrip
        stripHidden={stripHidden}
        onToggleStripHidden={() => setStripHidden((s) => !s)}
        selectedDay={selectedDay}
        onSelectDay={onSelectDay}
        appointments={appointments}
        selectedAppointmentId={selectedAppointmentId}
        onSelectAppointment={onPickAppointment}
        loading={apptsLoading}
      />

      {isViewingPastDay && (
        <p className="text-sm text-text-secondary rounded-[var(--radius-md)] border border-border/60 bg-background-secondary/50 px-3 py-2">
          {t('pastDayNotice')}
        </p>
      )}

      <div className="grid grid-cols-1 xl:grid-cols-[minmax(300px,380px)_minmax(0,1fr)] gap-4 items-start">
        <div className="space-y-3 min-w-0 xl:max-w-[380px]">
          {selectedAppointment ? (
            <div className="surface-card p-3 space-y-0.5">
              <p className="text-[10px] uppercase tracking-wide text-text-muted">{t('selectedPatient')}</p>
              <p className="text-base font-semibold text-text-primary">
                {selectedAppointment.patientFirstName} {selectedAppointment.patientLastName}
              </p>
              <p className="text-[11px] text-text-secondary">
                {t('purposeLabel')}{' '}
                <span className="capitalize text-text-primary">
                  {t(TREATMENT_TYPE_KEYS[selectedAppointment.purpose as keyof typeof TREATMENT_TYPE_KEYS] ?? selectedAppointment.purpose)}
                </span>
              </p>
            </div>
          ) : (
            <div className="surface-card p-3 text-sm text-text-muted">
              {apptsLoading ? t('loadingAppointments') : t('selectDayWithAppointment')}
            </div>
          )}

          <TreatmentPreviewCard
            draft={currentDraftPreview}
            disabled={!selectedAppointment}
            onPreview={openCurrentDraftPreview}
          />

          <PastTreatmentsPanel
            items={history}
            loading={historyLoading}
            onReviewTreatment={(t) => openPreview(t, 'readonly')}
          />
        </div>

        <div className="space-y-3 min-w-0 w-full">
          <FdiToothChart
            selected={selectedTeethSet}
            onToggle={(fdi) => {
              if (!canEditTreatmentForDay) return;
              setCases((prev) =>
                prev.map((c) => {
                  if (c.clientId !== activeCaseId) return c;
                  const set = new Set(c.teeth);
                  if (set.has(fdi)) set.delete(fdi);
                  else set.add(fdi);
                  return { ...c, teeth: [...set].sort() as FdiToothId[] };
                }),
              );
            }}
            disabled={!canEditTreatmentForDay}
          />

          <TreatmentCasesEditor
            cases={cases}
            activeCaseId={activeCaseId}
            onActiveCaseChange={setActiveCaseId}
            onCasesChange={setCases}
            disabled={!canEditTreatmentForDay}
            canEdit={canEdit}
            isDirty={isDirty}
            saveBusy={saveBusy}
            sendBusyId={sendBusyId}
            uploadBusy={uploadBusyCaseId === activeCaseId}
            orgs={orgs}
            organizationSearch={organizationSearch}
            onOrganizationSearchChange={setOrganizationSearch}
            recentOrganizationIds={recentOrganizationIds}
            onRecentOrganizationPick={(orgId) => {
              setCases((prev) =>
                prev.map((c) => {
                  if (c.clientId !== activeCaseId || c.sentAt) return c;
                  if (c.sendToOrganizationIds.includes(orgId)) return c;
                  return { ...c, sendToOrganizationIds: [...c.sendToOrganizationIds, orgId] };
                }),
              );
            }}
            onAddCase={() => {
              const nextCase = newCase();
              setCases((prev) => [...prev, nextCase]);
              setActiveCaseId(nextCase.clientId);
            }}
            onPreview={openCurrentDraftPreview}
            onSave={() => void handleSaveAll()}
            onSendCase={(c) => void handleSendCase(c)}
            onUploadFiles={(files) => void uploadForCase(activeCaseId, files ?? [])}
          />
        </div>
      </div>

      <TreatmentPreviewDialog
        open={previewOpen}
        onClose={() => setPreviewOpen(false)}
        treatment={
          previewMode === 'editable' && currentDraftPreview ? currentDraftPreview : previewTreatment
        }
        mode={previewMode}
        orgs={orgs}
        sendBusyCaseId={sendBusyId}
        uploadBusyCaseId={uploadBusyCaseId}
        onAttach={(caseKey, files) => uploadForCase(caseKey, files)}
        onSend={(caseKey, organizationIds) => {
          const c = cases.find((item) => item.clientId === caseKey);
          if (!c) return;
          void handleSendCase({ ...c, sendToOrganizationIds: organizationIds });
        }}
        getCaseOrgIds={getCaseOrgIds}
        onToggleCaseOrg={toggleCaseOrg}
      />
    </div>
  );
}
