'use client';

import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { useTranslations } from 'next-intl';
import type { FdiToothId, LabCaseToothProsthesisDraft, ToothSelectionGroup } from '@/types/treatment';
import type { ProsthesisCatalogEntry } from '@/types/treatment-catalog';
import { FdiToothChart } from '@/components/ui/treatment/FdiToothChart';
import {
  ProsthesisJobPopover,
  type PickerStep,
} from '@/components/ui/treatment/ProsthesisJobPopover';
import {
  applyLeafToJobs,
  archSentinels,
  archTargetFromJobs,
  catalogByCode,
  clearJobsForTeeth,
  codesOnTooth,
  ensureTeethInGroups,
  hasArchJobs,
  isArchSentinel,
  jobsAllowCrownAddon,
  removeTeethFromGroups,
  retargetArchJobs,
  toothRegionColors,
  uniqueCodesOnGroup,
  writeArchJobs,
  writeJobsForTeeth,
  type AddonSlot,
  type ArchTarget,
  type PickerScope,
} from '@/components/treatment/prosthesisTree';
import {
  deriveTeethFromGroups,
  groupsFromFlatTeeth,
  linkedEdgesFromGroups,
  teethBetweenInclusive,
} from '@/components/treatment/toothSelectionGroups';

interface ProsthesisAssignChartProps {
  groups: ToothSelectionGroup[];
  toothProsthesis: LabCaseToothProsthesisDraft[];
  detailClientId: string;
  catalog: readonly ProsthesisCatalogEntry[];
  disabled?: boolean;
  className?: string;
  headerControl?: ReactNode;
  onToggleLink?: (a: FdiToothId, b: FdiToothId) => void;
  onChange: (next: {
    groups: ToothSelectionGroup[];
    toothProsthesis: LabCaseToothProsthesisDraft[];
  }) => void;
}

export function ProsthesisAssignChart({
  groups,
  toothProsthesis,
  detailClientId,
  catalog,
  disabled,
  className,
  headerControl,
  onToggleLink,
  onChange,
}: ProsthesisAssignChartProps) {
  const tTreatment = useTranslations('treatment');
  const [open, setOpen] = useState(false);
  const [mobile, setMobile] = useState(false);
  const [anchor, setAnchor] = useState<FdiToothId | null>(null);
  const [step, setStep] = useState<PickerStep>({ kind: 'category' });
  const [scope, setScope] = useState<PickerScope>('tooth');
  const [lockedArch, setLockedArch] = useState<ArchTarget>('upper');
  const brushRef = useRef<string[]>([]);
  const rangeAnchorRef = useRef<FdiToothId | null>(null);
  const byCode = useMemo(() => catalogByCode(catalog), [catalog]);

  useEffect(() => {
    const mq = window.matchMedia('(max-width: 767px)');
    const sync = () => setMobile(mq.matches);
    sync();
    mq.addEventListener('change', sync);
    return () => mq.removeEventListener('change', sync);
  }, []);

  const selected = useMemo(() => new Set(deriveTeethFromGroups(groups)), [groups]);
  const linkedEdges = useMemo(() => linkedEdgesFromGroups(groups), [groups]);
  const detailRows = useMemo(
    () => toothProsthesis.filter((r) => r.detailClientId === detailClientId),
    [toothProsthesis, detailClientId],
  );
  const regionColors = useMemo(
    () => toothRegionColors(detailRows, catalog),
    [detailRows, catalog],
  );
  const persistedArch = useMemo(
    () => archTargetFromJobs(toothProsthesis, detailClientId),
    [toothProsthesis, detailClientId],
  );
  const archHighlight = open && scope === 'arch' ? lockedArch : persistedArch;
  const archWork = hasArchJobs(toothProsthesis, detailClientId);

  const jobCodes =
    scope === 'arch'
      ? uniqueCodesOnGroup(toothProsthesis, detailClientId, archSentinels(lockedArch))
      : anchor
        ? codesOnTooth(toothProsthesis, detailClientId, anchor)
        : [];

  function groupIdForTooth(tooth: string, nextGroups: ToothSelectionGroup[]): string {
    return nextGroups.find((g) => g.teeth.includes(tooth as FdiToothId))?.groupId ?? '';
  }

  function commit(nextGroups: ToothSelectionGroup[], nextRows: LabCaseToothProsthesisDraft[]) {
    onChange({ groups: nextGroups, toothProsthesis: nextRows });
  }

  function applyCodes(teeth: FdiToothId[], codes: string[]) {
    if (codes.length === 0 || teeth.length === 0) return;
    const nextGroups = ensureTeethInGroups(groups, teeth);
    const nextRows = writeJobsForTeeth(
      toothProsthesis,
      detailClientId,
      teeth,
      codes,
      (tooth) => groupIdForTooth(tooth, nextGroups),
    );
    commit(nextGroups, nextRows);
  }

  function rememberBrushFromTooth(tooth: FdiToothId | null) {
    if (!tooth) return;
    const codes = codesOnTooth(toothProsthesis, detailClientId, tooth);
    if (codes.length === 0) return;
    brushRef.current = codes;
    rangeAnchorRef.current = tooth;
  }

  function openToothPicker(fdi: FdiToothId) {
    setScope('tooth');
    setAnchor(fdi);
    setStep({ kind: 'category' });
    setOpen(true);
  }

  function handleAssignPointer(
    fdi: FdiToothId,
    event: { shiftKey: boolean; ctrlKey: boolean; paint: boolean },
  ) {
    if (disabled) return;
    if (event.paint || event.ctrlKey) {
      if (brushRef.current.length === 0) return;
      applyCodes([fdi], brushRef.current);
      return;
    }
    if (event.shiftKey) {
      if (brushRef.current.length === 0) return;
      const start = rangeAnchorRef.current;
      if (!start || start === fdi) {
        rangeAnchorRef.current = fdi;
        applyCodes([fdi], brushRef.current);
        return;
      }
      const span = teethBetweenInclusive(start, fdi);
      if (!span || span.length === 0) return;
      applyCodes(span, brushRef.current);
      rangeAnchorRef.current = fdi;
      return;
    }

    openToothPicker(fdi);
  }

  function handleArchClick(arch: 'upper' | 'lower') {
    if (disabled) return;
    setScope('arch');
    setLockedArch(archTargetFromJobs(toothProsthesis, detailClientId) ?? arch);
    setStep({ kind: 'category' });
    setOpen(true);
  }

  function handleLockedArchChange(next: ArchTarget) {
    if (next === lockedArch) return;
    const previous = lockedArch;
    setLockedArch(next);
    setStep((current) =>
      current.kind === 'subcategory' || current.kind === 'leaves'
        ? { ...current, arch: next }
        : current,
    );
    const nextRows = retargetArchJobs(toothProsthesis, detailClientId, previous, next);
    if (nextRows !== toothProsthesis) {
      commit(groups, nextRows);
    }
  }

  function handlePickLeaf(code: string) {
    if (scope === 'arch') {
      commit(groups, writeArchJobs(toothProsthesis, detailClientId, lockedArch, code));
      setStep({ kind: 'category' });
      return;
    }
    if (!anchor) return;
    const base = codesOnTooth(toothProsthesis, detailClientId, anchor);
    const nextCodes = applyLeafToJobs(base, code, byCode);
    applyCodes([anchor], nextCodes);
    setStep({ kind: 'category' });
  }

  function handlePickAddon(code: string | null, kind: AddonSlot) {
    if (!anchor || scope === 'arch' || kind !== 'crown') return;
    const current = codesOnTooth(toothProsthesis, detailClientId, anchor).filter((c) => {
      return byCode.get(c)?.stackGroup !== 'restoration';
    });
    const next = code ? applyLeafToJobs(current, code, byCode) : current;
    if (next.length === 0) {
      commit(
        removeTeethFromGroups(groups, [anchor]),
        clearJobsForTeeth(toothProsthesis, detailClientId, [anchor]),
      );
      setStep({ kind: 'category' });
      return;
    }
    applyCodes([anchor], next);
    setStep({ kind: 'category' });
  }

  function handleRemoveJob(code: string) {
    if (scope === 'arch') {
      const rest = jobCodes.filter((c) => c !== code);
      if (rest.length === 0) {
        commit(
          groups,
          clearJobsForTeeth(toothProsthesis, detailClientId, archSentinels('both')),
        );
        return;
      }
      commit(groups, writeArchJobs(toothProsthesis, detailClientId, lockedArch, rest[0]));
      return;
    }
    const tooth = anchor;
    if (!tooth) return;
    const next = codesOnTooth(toothProsthesis, detailClientId, tooth).filter((c) => c !== code);
    if (next.length === 0) {
      commit(
        removeTeethFromGroups(groups, [tooth]),
        clearJobsForTeeth(toothProsthesis, detailClientId, [tooth]),
      );
      if (rangeAnchorRef.current === tooth) {
        brushRef.current = [];
        rangeAnchorRef.current = null;
      }
      return;
    }
    applyCodes([tooth], next);
  }

  function handleClear() {
    if (scope === 'arch') {
      commit(
        groups,
        clearJobsForTeeth(toothProsthesis, detailClientId, archSentinels('both')),
      );
      setOpen(false);
      return;
    }
    const teeth = anchor ? [anchor] : [];
    if (teeth.length === 0) return;
    commit(
      removeTeethFromGroups(groups, teeth),
      clearJobsForTeeth(toothProsthesis, detailClientId, teeth),
    );
    brushRef.current = [];
    rangeAnchorRef.current = null;
    setOpen(false);
    setAnchor(null);
  }

  function handleClosePicker() {
    if (scope === 'tooth') {
      rememberBrushFromTooth(anchor);
    }
    setOpen(false);
  }

  function handleReset() {
    if (disabled || (selected.size === 0 && !archWork)) return;
    if (!window.confirm(tTreatment('confirmResetChart'))) return;
    const fdiTeeth = [...selected];
    const withoutFdi = clearJobsForTeeth(toothProsthesis, detailClientId, fdiTeeth);
    const nextRows = withoutFdi.filter(
      (row) => !(row.detailClientId === detailClientId && isArchSentinel(row.tooth)),
    );
    commit(groupsFromFlatTeeth([]), nextRows);
    brushRef.current = [];
    rangeAnchorRef.current = null;
    setOpen(false);
    setAnchor(null);
  }

  const selectedSummary = (() => {
    const parts: string[] = [];
    if (persistedArch === 'upper' || persistedArch === 'both') {
      parts.push(tTreatment('selectedArchUpper'));
    }
    if (persistedArch === 'lower' || persistedArch === 'both') {
      parts.push(tTreatment('selectedArchLower'));
    }
    if (selected.size > 0) {
      parts.push([...selected].sort().join(', '));
    }
    return parts.length ? parts.join(' · ') : tTreatment('selectedEmpty');
  })();

  return (
    <div>
      <FdiToothChart
        className={className}
        selected={selected}
        linkedEdges={linkedEdges}
        disabled={disabled}
        crownColors={regionColors.crown}
        rootColors={regionColors.root}
        archHighlight={archHighlight}
        headerControl={headerControl}
        selectedSummary={selectedSummary}
        hasResetWork={selected.size > 0 || archWork}
        onArchClick={handleArchClick}
        overlay={
          <ProsthesisJobPopover
            open={open}
            mobile={mobile}
            catalog={catalog}
            step={step}
            scope={scope}
            lockedArch={scope === 'arch' ? lockedArch : undefined}
            jobCodes={jobCodes}
            tooth={anchor}
            allowCrownAddon={scope === 'tooth' && jobsAllowCrownAddon(jobCodes, byCode)}
            onStep={setStep}
            onLockedArchChange={handleLockedArchChange}
            onPickLeaf={handlePickLeaf}
            onPickAddon={handlePickAddon}
            onRemoveJob={handleRemoveJob}
            onClear={handleClear}
            onClose={handleClosePicker}
          />
        }
        onAssignPointer={handleAssignPointer}
        onToggleLink={onToggleLink}
        onReset={handleReset}
        resetDisabled={disabled}
      />
    </div>
  );
}
