import React, { useState, useEffect, useCallback, useRef } from 'react';
import {
  Camera, Upload, X, ZoomIn, Trash2, ChevronLeft, ChevronRight,
  CheckCircle, Circle, SplitSquareHorizontal, Loader2, RefreshCw, Pen,
  FileDown, Plus, CalendarPlus
} from 'lucide-react';
import { jsPDF } from 'jspdf';
import { HybridBackend } from '../services/backend.ts';
import { useToast } from '../contexts/ToastContext.tsx';
import { useConfirm } from '../contexts/ConfirmContext.tsx';
import { PhotoAnnotator } from './PhotoAnnotator.tsx';
import { CameraCapture } from '../components/CameraCapture.tsx';

// ─── Constantes ───────────────────────────────────────────────────

export const ORTO_SLOTS = [
  { key: 'extrafrontal_sorrindo',  label: 'FRONTAL SORRINDO',  cat: 'EXTRAORAL' },
  { key: 'extrafrontal_repouso',   label: 'FRONTAL REPOUSO',   cat: 'EXTRAORAL' },
  { key: 'extraperfil_direito',    label: 'PERFIL DIREITO',    cat: 'EXTRAORAL' },
  { key: 'extraperfil_esquerdo',   label: 'PERFIL ESQUERDO',   cat: 'EXTRAORAL' },
  { key: 'extraperfil_sorrindo',   label: 'PERFIL SORRINDO',   cat: 'EXTRAORAL' },
  { key: 'intrafrontal_oclusao',   label: 'FRONTAL OCLUSÃO',   cat: 'INTRAORAL' },
  { key: 'intralateral_direita',   label: 'LATERAL DIREITA',   cat: 'INTRAORAL' },
  { key: 'intralateral_esquerda',  label: 'LATERAL ESQUERDA',  cat: 'INTRAORAL' },
  { key: 'intraoclusal_superior',  label: 'OCLUSAL SUPERIOR',  cat: 'INTRAORAL' },
  { key: 'intraoclusal_inferior',  label: 'OCLUSAL INFERIOR',  cat: 'INTRAORAL' },
  { key: 'complinha_media',        label: 'LINHA MÉDIA',       cat: 'COMPLEMENTAR' },
  { key: 'compoverjet',            label: 'OVERJET',           cat: 'COMPLEMENTAR' },
  { key: 'compoverbite',           label: 'OVERBITE',          cat: 'COMPLEMENTAR' },
  { key: 'compmordida_cruzada',    label: 'MORDIDA CRUZADA',   cat: 'COMPLEMENTAR' },
  { key: 'compmordida_aberta',     label: 'MORDIDA ABERTA',    cat: 'COMPLEMENTAR' },
] as const;

// Sessões especiais (marcos do tratamento). Entre elas ficam as sessões MENSAIS
// (orto é mensal → uma sessão de fotos por mês para acompanhar a evolução).
const SPECIAL_SESSIONS: { tipo: string; label: string }[] = [
  { tipo: 'inicial',       label: 'INICIAL' },
  { tipo: 'finalizacao',   label: 'FINALIZAÇÃO' },
  { tipo: 'pos_contencao', label: 'PÓS-CONTENÇÃO' },
];

const MESES_PT = ['JAN', 'FEV', 'MAR', 'ABR', 'MAI', 'JUN', 'JUL', 'AGO', 'SET', 'OUT', 'NOV', 'DEZ'];

// ym = 'YYYY-MM'
const monthTipo = (ym: string) => `mes_${ym}`;
const monthLabel = (ym: string) => {
  const [y, m] = ym.split('-');
  return `${MESES_PT[Number(m) - 1] || m}/${y}`;
};
const currentYm = () => new Date().toISOString().slice(0, 7);

function sessionLabel(s: { tipo: string; rotulo?: string }): string {
  if (s.rotulo) return s.rotulo;
  const sp = SPECIAL_SESSIONS.find(x => x.tipo === s.tipo);
  if (sp) return sp.label;
  if (s.tipo.startsWith('mes_')) return monthLabel(s.tipo.slice(4));
  return s.tipo.toUpperCase();
}

// Ordena: INICIAL primeiro, depois meses por data, depois FINALIZAÇÃO / PÓS-CONTENÇÃO.
function sessionSortKey(s: { tipo: string; data_ref?: string }): string {
  if (s.tipo === 'inicial') return '0';
  if (s.tipo === 'finalizacao') return '8';
  if (s.tipo === 'pos_contencao') return '9';
  if (s.tipo.startsWith('mes_')) return `1_${s.tipo.slice(4)}`;
  return `5_${s.data_ref || s.tipo}`;
}

const CAT_COLORS: Record<string, string> = {
  EXTRAORAL:    'text-teal-600 bg-teal-50 border-teal-200',
  INTRAORAL:    'text-purple-600 bg-purple-50 border-purple-200',
  COMPLEMENTAR: 'text-emerald-600 bg-emerald-50 border-emerald-200',
};

interface OrtoFoto {
  id: string;
  slot: string;
  url: string;
  original_name?: string;
}

interface OrtoSessao {
  id: string;
  tipo: string;
  rotulo?: string;
  data_ref?: string;
  fotos: OrtoFoto[];
}

// ─── Viewer Modal ─────────────────────────────────────────────────

const PhotoViewer: React.FC<{
  foto: OrtoFoto;
  slotLabel: string;
  onClose: () => void;
  onDelete: () => void;
}> = ({ foto, slotLabel, onClose, onDelete }) => {
  const confirm = useConfirm();
  const [annotating, setAnnotating] = useState(false);

  useEffect(() => {
    const h = (e: KeyboardEvent) => { if (e.key === 'Escape' && !annotating) onClose(); };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [onClose, annotating]);

  if (annotating) {
    return <PhotoAnnotator fotoId={foto.id} fotoUrl={foto.url} slotLabel={slotLabel} onClose={() => setAnnotating(false)} />;
  }

  return (
    <div className="fixed inset-0 bg-black/90 z-[70] flex flex-col">
      <div className="flex items-center justify-between px-4 py-3 flex-shrink-0">
        <span className="text-white font-black text-xs uppercase">{slotLabel}</span>
        <div className="flex items-center gap-2">
          <button
            onClick={() => setAnnotating(true)}
            className="flex items-center gap-1.5 px-3 py-1.5 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg text-[11px] font-black uppercase transition-colors"
          >
            <Pen size={13} /> ANOTAR
          </button>
          <button
            onClick={async () => { if (await confirm('EXCLUIR ESTA FOTO?')) { onDelete(); onClose(); } }}
            className="flex items-center gap-1.5 px-3 py-1.5 bg-red-600 hover:bg-red-700 text-white rounded-lg text-[11px] font-black uppercase transition-colors"
          >
            <Trash2 size={13} /> EXCLUIR
          </button>
          <button onClick={onClose} className="p-2 hover:bg-white/10 rounded-lg transition-colors text-white">
            <X size={20} />
          </button>
        </div>
      </div>
      <div className="flex-1 flex items-center justify-center p-4 min-h-0">
        <img
          src={foto.url}
          alt={slotLabel}
          className="max-w-full max-h-full object-contain rounded-xl select-none"
          draggable={false}
        />
      </div>
    </div>
  );
};

// ─── Comparador lado a lado ───────────────────────────────────────

// Comparador cross-sessão: mesmo slot em duas sessões (meses) diferentes.
// Orto é mensal, então o uso natural é comparar a evolução: ex. INICIAL × JUN/2026.
const Comparator: React.FC<{
  sessoes: OrtoSessao[];
  sessaoInicial?: string;
  onClose: () => void;
}> = ({ sessoes, sessaoInicial, onClose }) => {
  const comFotos = sessoes.filter(s => s.fotos.length);
  const slotsDisp = ORTO_SLOTS.filter(sl => comFotos.some(s => s.fotos.some(f => f.slot === sl.key)));
  const [slot, setSlot] = useState((slotsDisp[0] || ORTO_SLOTS[0]).key as string);
  const [tipoA, setTipoA] = useState(sessaoInicial && comFotos.some(s => s.tipo === sessaoInicial) ? sessaoInicial : (comFotos[0]?.tipo || ''));
  const [tipoB, setTipoB] = useState(comFotos[comFotos.length - 1]?.tipo || '');
  const [modo, setModo] = useState<'lado' | 'slider' | 'sobre' | 'evolucao'>('lado');
  const [pos, setPos] = useState(50);
  const [op, setOp] = useState(50);

  const fotoOf = (tipo: string) => comFotos.find(s => s.tipo === tipo)?.fotos.find(f => f.slot === slot);
  const fotoA = fotoOf(tipoA);
  const fotoB = fotoOf(tipoB);
  const labelOf = (tipo: string) => { const s = sessoes.find(x => x.tipo === tipo); return s ? sessionLabel(s) : tipo; };
  const slotLabel = ORTO_SLOTS.find(s => s.key === slot)?.label || slot;

  const MODOS: { id: 'lado' | 'slider' | 'sobre' | 'evolucao'; label: string }[] = [
    { id: 'lado', label: 'LADO A LADO' },
    { id: 'slider', label: 'SLIDER' },
    { id: 'sobre', label: 'SOBREPOSIÇÃO' },
    { id: 'evolucao', label: 'EVOLUÇÃO' },
  ];

  return (
    <div className="fixed inset-0 bg-black/90 z-[70] flex flex-col">
      <div className="flex items-center justify-between px-4 py-3 flex-shrink-0 border-b border-white/10">
        <span className="text-white font-black text-sm uppercase flex items-center gap-2">
          <SplitSquareHorizontal size={16} /> COMPARADOR — {slotLabel}
        </span>
        <button onClick={onClose} className="p-2 hover:bg-white/10 rounded-lg text-white transition-colors"><X size={20} /></button>
      </div>

      {/* Controles */}
      <div className="flex flex-wrap gap-3 px-4 py-3 flex-shrink-0 bg-black/30 items-end">
        <div className="flex-1 min-w-[120px]">
          <p className="text-[10px] text-white/50 font-bold uppercase mb-1">FOTO (SLOT)</p>
          <select value={slot} onChange={e => setSlot(e.target.value)} className="w-full bg-white/10 text-white text-[11px] font-bold rounded-lg px-2 py-1.5 border border-white/20 uppercase">
            {ORTO_SLOTS.map(s => (
              <option key={s.key} value={s.key} className="bg-gray-900">{s.label} {slotsDisp.some(d => d.key === s.key) ? '✓' : ''}</option>
            ))}
          </select>
        </div>
        <div className="flex-1 min-w-[110px]">
          <p className="text-[10px] text-white/50 font-bold uppercase mb-1">SESSÃO A</p>
          <select value={tipoA} onChange={e => setTipoA(e.target.value)} className="w-full bg-white/10 text-white text-[11px] font-bold rounded-lg px-2 py-1.5 border border-white/20 uppercase">
            {comFotos.map(s => <option key={s.tipo} value={s.tipo} className="bg-gray-900">{sessionLabel(s)}</option>)}
          </select>
        </div>
        <div className="flex-1 min-w-[110px]">
          <p className="text-[10px] text-white/50 font-bold uppercase mb-1">SESSÃO B</p>
          <select value={tipoB} onChange={e => setTipoB(e.target.value)} className="w-full bg-white/10 text-white text-[11px] font-bold rounded-lg px-2 py-1.5 border border-white/20 uppercase">
            {comFotos.map(s => <option key={s.tipo} value={s.tipo} className="bg-gray-900">{sessionLabel(s)}</option>)}
          </select>
        </div>
        <div className="flex gap-1">
          {MODOS.map(m => (
            <button key={m.id} onClick={() => setModo(m.id)}
              className={`px-2.5 py-1.5 rounded-lg text-[10px] font-black uppercase border transition-colors ${modo === m.id ? 'bg-white text-gray-900 border-white' : 'bg-white/10 text-white/70 border-white/20 hover:bg-white/20'}`}>
              {m.label}
            </button>
          ))}
        </div>
      </div>

      {/* Visualização */}
      <div className="flex-1 p-4 min-h-0">
        {modo === 'evolucao' ? (
          <div className="h-full overflow-x-auto overflow-y-hidden">
            <div className="flex gap-3 h-full pb-2" style={{ minWidth: 'min-content' }}>
              {comFotos.length === 0 ? (
                <div className="w-full flex flex-col items-center justify-center text-white/30 gap-2"><Camera size={36} /><span className="text-xs font-bold uppercase">SEM FOTOS</span></div>
              ) : comFotos.map(s => {
                const foto = s.fotos.find(ff => ff.slot === slot);
                return (
                  <div key={s.tipo} className="flex-shrink-0 w-52 bg-black/40 rounded-xl flex flex-col overflow-hidden border border-white/10">
                    <p className="text-[10px] font-black text-white/60 uppercase px-3 py-1.5 bg-black/20 text-center flex-shrink-0">{sessionLabel(s)}</p>
                    <div className="flex-1 flex items-center justify-center p-2 min-h-0">
                      {foto ? <img src={foto.url} alt="" className="max-w-full max-h-full object-contain rounded-lg" />
                            : <div className="flex flex-col items-center gap-1 text-white/30"><Camera size={22} /><span className="text-[9px] font-bold uppercase">SEM FOTO</span></div>}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ) : !fotoA && !fotoB ? (
          <div className="h-full flex flex-col items-center justify-center text-white/30 gap-2">
            <Camera size={36} /><span className="text-xs font-bold uppercase">SEM FOTO DESTE SLOT NAS SESSÕES ESCOLHIDAS</span>
          </div>
        ) : modo === 'lado' ? (
          <div className="h-full grid grid-cols-2 gap-2">
            {[{ foto: fotoA, lbl: labelOf(tipoA) }, { foto: fotoB, lbl: labelOf(tipoB) }].map((c, i) => (
              <div key={i} className="bg-black/40 rounded-xl flex flex-col overflow-hidden border border-white/10">
                <p className="text-[10px] font-black text-white/60 uppercase px-3 py-1.5 flex-shrink-0 bg-black/20">{c.lbl}</p>
                <div className="flex-1 flex items-center justify-center p-2 min-h-0">
                  {c.foto ? <img src={c.foto.url} alt="" className="max-w-full max-h-full object-contain rounded-lg" />
                          : <div className="flex flex-col items-center gap-2 text-white/30"><Camera size={28} /><span className="text-[10px] font-bold uppercase">SEM FOTO</span></div>}
                </div>
              </div>
            ))}
          </div>
        ) : modo === 'slider' ? (
          <div className="h-full flex flex-col gap-2">
            <div className="relative flex-1 min-h-0 mx-auto max-w-3xl w-full overflow-hidden rounded-xl border border-white/10 bg-black/40 select-none">
              {fotoA && <img src={fotoA.url} alt="" className="absolute inset-0 w-full h-full object-contain" />}
              {fotoB && <div className="absolute inset-0 overflow-hidden" style={{ clipPath: `inset(0 0 0 ${pos}%)` }}>
                <img src={fotoB.url} alt="" className="absolute inset-0 w-full h-full object-contain" />
              </div>}
              <div className="absolute top-0 bottom-0 w-0.5 bg-white/80 pointer-events-none" style={{ left: `${pos}%` }} />
              <span className="absolute top-2 left-2 bg-black/60 text-white text-[9px] font-black uppercase px-2 py-0.5 rounded">{labelOf(tipoA)}</span>
              <span className="absolute top-2 right-2 bg-black/60 text-white text-[9px] font-black uppercase px-2 py-0.5 rounded">{labelOf(tipoB)}</span>
            </div>
            <input type="range" min={0} max={100} value={pos} onChange={e => setPos(Number(e.target.value))} className="w-full max-w-3xl mx-auto" />
          </div>
        ) : (
          <div className="h-full flex flex-col gap-2">
            <div className="relative flex-1 min-h-0 mx-auto max-w-3xl w-full overflow-hidden rounded-xl border border-white/10 bg-black/40">
              {fotoA && <img src={fotoA.url} alt="" className="absolute inset-0 w-full h-full object-contain" />}
              {fotoB && <img src={fotoB.url} alt="" className="absolute inset-0 w-full h-full object-contain" style={{ opacity: op / 100 }} />}
            </div>
            <div className="flex items-center gap-3 max-w-3xl mx-auto w-full">
              <span className="text-[9px] font-black text-white/60 uppercase">{labelOf(tipoA)}</span>
              <input type="range" min={0} max={100} value={op} onChange={e => setOp(Number(e.target.value))} className="flex-1" />
              <span className="text-[9px] font-black text-white/60 uppercase">{labelOf(tipoB)}</span>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

// ─── Grade de slots ───────────────────────────────────────────────

const SlotGrid: React.FC<{
  sessao: OrtoSessao;
  onUpload: (slot: string, file: File) => Promise<void>;
  onView: (foto: OrtoFoto, slotLabel: string) => void;
  onRequestCapture: (slot: string) => void;
  uploading: Record<string, boolean>;
}> = ({ sessao, onUpload, onView, onRequestCapture, uploading }) => {
  const fotosMap = Object.fromEntries(sessao.fotos.map(f => [f.slot, f]));
  const inputRefs = useRef<Record<string, HTMLInputElement | null>>({});

  const cats = ['EXTRAORAL', 'INTRAORAL', 'COMPLEMENTAR'];

  return (
    <div className="space-y-5">
      {cats.map(cat => (
        <div key={cat}>
          <p className={`text-[10px] font-black uppercase px-2 py-1 rounded-lg border w-fit mb-3 ${CAT_COLORS[cat]}`}>{cat}</p>
          <div className="grid grid-cols-3 sm:grid-cols-4 lg:grid-cols-5 gap-3">
            {ORTO_SLOTS.filter(s => s.cat === cat).map(slot => {
              const foto = fotosMap[slot.key];
              const isUploading = uploading[slot.key];

              return (
                <div key={slot.key} className="flex flex-col gap-1">
                  {/* Slot box */}
                  <div
                    onClick={() => foto ? onView(foto, slot.label) : onRequestCapture(slot.key)}
                    className={`
                      relative aspect-[4/3] rounded-xl border-2 overflow-hidden cursor-pointer
                      transition-all duration-200 group
                      ${foto
                        ? 'border-transparent shadow-md hover:shadow-lg'
                        : 'border-dashed border-gray-300 hover:border-teal-400 bg-gray-50 hover:bg-teal-50'
                      }
                    `}
                  >
                    {isUploading ? (
                      <div className="absolute inset-0 flex items-center justify-center bg-white/80">
                        <Loader2 size={22} className="animate-spin text-teal-500" />
                      </div>
                    ) : foto ? (
                      <>
                        <img src={foto.url} alt={slot.label} className="w-full h-full object-cover" />
                        <div className="absolute inset-0 bg-black/0 group-hover:bg-black/30 transition-all flex items-center justify-center">
                          <ZoomIn size={22} className="text-white opacity-0 group-hover:opacity-100 transition-opacity" />
                        </div>
                        <div className="absolute top-1.5 right-1.5">
                          <CheckCircle size={16} className="text-white drop-shadow" />
                        </div>
                      </>
                    ) : (
                      <div className="absolute inset-0 flex flex-col items-center justify-center gap-1 text-gray-400 group-hover:text-teal-500 transition-colors">
                        <Camera size={20} />
                        <Upload size={13} />
                      </div>
                    )}
                  </div>

                  {/* Label */}
                  <p className="text-[9px] font-black text-gray-500 uppercase text-center leading-tight">{slot.label}</p>

                  {/* Input oculto */}
                  <input
                    ref={el => { inputRefs.current[slot.key] = el; }}
                    type="file"
                    accept="image/jpeg,image/jpg,image/png,image/webp"
                    className="hidden"
                    onChange={async e => {
                      const file = e.target.files?.[0];
                      if (file) await onUpload(slot.key, file);
                      e.target.value = '';
                    }}
                  />
                </div>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
};

// ─── Barra de completude ──────────────────────────────────────────

const Completude: React.FC<{ sessao: OrtoSessao }> = ({ sessao }) => {
  const total = ORTO_SLOTS.length;
  const preenchidos = new Set(sessao.fotos.map(f => f.slot)).size;
  const pct = Math.round((preenchidos / total) * 100);
  const color = pct === 100 ? 'bg-emerald-500' : pct >= 60 ? 'bg-teal-500' : pct >= 30 ? 'bg-amber-500' : 'bg-gray-300';

  return (
    <div className="flex items-center gap-3">
      <div className="flex-1 bg-gray-100 rounded-full h-2">
        <div className={`${color} h-2 rounded-full transition-all duration-500`} style={{ width: `${pct}%` }} />
      </div>
      <span className="text-[11px] font-black text-gray-500 uppercase w-24 text-right">
        {preenchidos}/{total} — {pct}%
      </span>
    </div>
  );
};

// ─── Exportação da documentação ───────────────────────────────────

interface ExportFoto { id: string; url: string; slot: string; sessaoLabel: string; }

// Abre uma janela de impressão (o navegador salva como PDF) com as fotos
// agrupadas por sessão. Sem dependência extra.
function abrirDocumentoImpressao(titulo: string, fotos: ExportFoto[]) {
  const w = window.open('', '_blank');
  if (!w) return;
  const origin = window.location.origin;
  const slotLabel = (slot: string) => ORTO_SLOTS.find(s => s.key === slot)?.label || slot.toUpperCase();
  const grupos: { label: string; fotos: ExportFoto[] }[] = [];
  for (const f of fotos) {
    let g = grupos.find(x => x.label === f.sessaoLabel);
    if (!g) { g = { label: f.sessaoLabel, fotos: [] }; grupos.push(g); }
    g.fotos.push(f);
  }
  const cards = grupos.map(g => `
    <section class="sessao">
      <h2>${g.label}</h2>
      <div class="grid">
        ${g.fotos.map(f => `<figure><img src="${origin}${f.url}" /><figcaption>${slotLabel(f.slot)}</figcaption></figure>`).join('')}
      </div>
    </section>`).join('');
  w.document.write(`<!doctype html><html lang="pt-BR"><head><meta charset="utf-8" /><title>${titulo}</title>
    <style>
      *{box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;}
      body{margin:24px;color:#1f2937;}
      header{display:flex;justify-content:space-between;align-items:baseline;border-bottom:2px solid #0d9488;padding-bottom:8px;margin-bottom:16px;}
      header h1{font-size:18px;margin:0;text-transform:uppercase;letter-spacing:.5px;}
      header span{font-size:11px;color:#6b7280;}
      .sessao{margin-bottom:22px;break-inside:avoid;}
      .sessao h2{font-size:13px;text-transform:uppercase;color:#0d9488;margin:0 0 8px;border-left:4px solid #0d9488;padding-left:8px;}
      .grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;}
      figure{margin:0;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;break-inside:avoid;}
      figure img{width:100%;height:150px;object-fit:cover;display:block;}
      figcaption{font-size:9px;font-weight:bold;text-transform:uppercase;color:#6b7280;text-align:center;padding:4px;}
      @media print{ body{margin:0;} @page{margin:14mm;} }
    </style></head>
    <body>
      <header><h1>${titulo}</h1><span>Documentação ortodôntica · ${new Date().toLocaleDateString('pt-BR')}</span></header>
      ${cards || '<p>Nenhuma foto selecionada.</p>'}
      <script>
        window.addEventListener('load', function(){
          var imgs = Array.prototype.slice.call(document.images);
          Promise.all(imgs.map(function(i){ return i.complete ? 1 : new Promise(function(r){ i.onload=i.onerror=r; }); }))
            .then(function(){ setTimeout(function(){ window.print(); }, 300); });
        });
      </script>
    </body></html>`);
  w.document.close();
}

// Carrega imagem (mesma origem) e devolve dataURL JPEG via canvas.
function carregarDataUrl(url: string): Promise<string | null> {
  return new Promise(resolve => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => {
      try {
        const canvas = document.createElement('canvas');
        canvas.width = img.naturalWidth; canvas.height = img.naturalHeight;
        const ctx = canvas.getContext('2d');
        if (!ctx) return resolve(null);
        ctx.drawImage(img, 0, 0);
        resolve(canvas.toDataURL('image/jpeg', 0.85));
      } catch { resolve(null); }
    };
    img.onerror = () => resolve(null);
    img.src = url;
  });
}

// Gera um PDF nativo (jsPDF) com as fotos agrupadas por sessão, 3 por linha.
async function exportarPDF(titulo: string, fotos: ExportFoto[]) {
  const doc = new jsPDF({ unit: 'mm', format: 'a4' });
  const pageW = 210, pageH = 297, margin = 12, gap = 6, cols = 3;
  const colW = (pageW - margin * 2 - gap * (cols - 1)) / cols;
  const imgH = colW * 0.78;
  const cellH = imgH + 7;
  const slotLabel = (slot: string) => ORTO_SLOTS.find(s => s.key === slot)?.label || slot;

  doc.setFontSize(14); doc.setTextColor(31); doc.text(titulo, margin, margin + 4);
  doc.setFontSize(8); doc.setTextColor(120);
  doc.text('Documentação ortodôntica · ' + new Date().toLocaleDateString('pt-BR'), margin, margin + 9);
  doc.setDrawColor(79, 70, 229); doc.line(margin, margin + 12, pageW - margin, margin + 12);
  let y = margin + 18;

  const grupos: { label: string; fotos: ExportFoto[] }[] = [];
  for (const f of fotos) { let g = grupos.find(x => x.label === f.sessaoLabel); if (!g) { g = { label: f.sessaoLabel, fotos: [] }; grupos.push(g); } g.fotos.push(f); }

  for (const g of grupos) {
    if (y + 10 + cellH > pageH - margin) { doc.addPage(); y = margin; }
    doc.setFontSize(10); doc.setTextColor(79, 70, 229); doc.text(g.label.toUpperCase(), margin, y); y += 4;
    let col = 0;
    for (const f of g.fotos) {
      if (y + cellH > pageH - margin) { doc.addPage(); y = margin; col = 0; }
      const x = margin + col * (colW + gap);
      const data = await carregarDataUrl(window.location.origin + f.url);
      if (data) { try { doc.addImage(data, 'JPEG', x, y, colW, imgH); } catch { doc.setDrawColor(220); doc.rect(x, y, colW, imgH); } }
      else { doc.setDrawColor(220); doc.rect(x, y, colW, imgH); }
      doc.setFontSize(6.5); doc.setTextColor(110);
      doc.text(slotLabel(f.slot).toUpperCase(), x + colW / 2, y + imgH + 4, { align: 'center' });
      col++;
      if (col === cols) { col = 0; y += cellH; }
    }
    if (col !== 0) y += cellH;
    y += 4;
  }
  doc.save(titulo.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_|_$/g, '') + '.pdf');
}

const ExportModal: React.FC<{
  sessoes: OrtoSessao[];
  pacienteNome?: string;
  onClose: () => void;
}> = ({ sessoes, pacienteNome, onClose }) => {
  const ordenadas = [...sessoes].sort((a, b) => sessionSortKey(a).localeCompare(sessionSortKey(b)));
  const todas: ExportFoto[] = ordenadas.flatMap(s => s.fotos.map(f => ({ id: f.id, url: f.url, slot: f.slot, sessaoLabel: sessionLabel(s) })));
  const [sel, setSel] = useState<Set<string>>(new Set(todas.map(f => f.id)));
  const [gerando, setGerando] = useState(false);
  const slotLabel = (slot: string) => ORTO_SLOTS.find(s => s.key === slot)?.label || slot;
  const toggle = (id: string) => setSel(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const todasMarcadas = todas.length > 0 && sel.size === todas.length;
  const toggleTodas = () => setSel(todasMarcadas ? new Set() : new Set(todas.map(f => f.id)));
  const titulo = `DOCUMENTAÇÃO ORTODÔNTICA${pacienteNome ? ' — ' + pacienteNome.toUpperCase() : ''}`;
  const selecionadas = () => todas.filter(f => sel.has(f.id));
  const exportarPdf = async () => {
    const fotos = selecionadas();
    if (!fotos.length) return;
    setGerando(true);
    try { await exportarPDF(titulo, fotos); onClose(); }
    finally { setGerando(false); }
  };
  const imprimir = () => {
    const fotos = selecionadas();
    if (!fotos.length) return;
    abrirDocumentoImpressao(titulo, fotos);
    onClose();
  };

  return (
    <div className="fixed inset-0 bg-black/60 z-[70] flex items-center justify-center p-4 backdrop-blur-sm">
      <div className="bg-white rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] flex flex-col">
        <div className="flex items-center justify-between px-5 py-4 border-b border-gray-100 flex-shrink-0">
          <div className="flex items-center gap-2"><FileDown size={18} className="text-emerald-500" /><h3 className="font-black text-sm uppercase text-gray-800">EXPORTAR DOCUMENTAÇÃO</h3></div>
          <button onClick={onClose} className="p-1.5 hover:bg-gray-100 rounded-lg"><X size={18} /></button>
        </div>
        <div className="px-5 py-3 border-b border-gray-100 flex items-center justify-between">
          <button onClick={toggleTodas} className="text-[11px] font-black uppercase text-emerald-600 hover:text-emerald-800">
            {todasMarcadas ? 'DESMARCAR TUDO' : 'SELECIONAR TUDO (COMPLETA)'}
          </button>
          <span className="text-[10px] font-black text-gray-400 uppercase">{sel.size}/{todas.length} FOTOS</span>
        </div>
        <div className="flex-1 overflow-y-auto p-5 space-y-4 min-h-0">
          {todas.length === 0 ? (
            <p className="text-center text-gray-400 font-bold uppercase text-xs py-8">NENHUMA FOTO PARA EXPORTAR.</p>
          ) : ordenadas.filter(s => s.fotos.length).map(s => (
            <div key={s.id}>
              <p className="text-[10px] font-black text-emerald-600 uppercase mb-2">{sessionLabel(s)}</p>
              <div className="grid grid-cols-3 sm:grid-cols-4 gap-2">
                {s.fotos.map(f => {
                  const on = sel.has(f.id);
                  return (
                    <button key={f.id} onClick={() => toggle(f.id)} className={`relative aspect-square rounded-lg overflow-hidden border-2 transition-all ${on ? 'border-emerald-500' : 'border-transparent opacity-50'}`}>
                      <img src={f.url} alt={f.slot} className="w-full h-full object-cover" />
                      <span className={`absolute top-1 right-1 w-4 h-4 rounded-full flex items-center justify-center ${on ? 'bg-emerald-600' : 'bg-black/40'}`}>
                        {on && <CheckCircle size={12} className="text-white" />}
                      </span>
                      <span className="absolute bottom-0 inset-x-0 bg-black/50 text-white text-[7px] font-bold uppercase text-center py-0.5 truncate">{slotLabel(f.slot)}</span>
                    </button>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
        <div className="px-5 pb-5 pt-3 border-t border-gray-100 flex gap-2 flex-shrink-0">
          <button onClick={onClose} disabled={gerando} className="px-4 py-2.5 border border-gray-200 rounded-xl text-[11px] font-black uppercase hover:bg-gray-50 disabled:opacity-40">CANCELAR</button>
          <button onClick={imprimir} disabled={sel.size === 0 || gerando} className="px-4 py-2.5 border border-gray-200 rounded-xl text-[11px] font-black uppercase text-gray-600 hover:bg-gray-50 disabled:opacity-40">IMPRIMIR</button>
          <button onClick={exportarPdf} disabled={sel.size === 0 || gerando} className="flex-1 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white rounded-xl text-[11px] font-black uppercase disabled:opacity-40 flex items-center justify-center gap-2">
            {gerando ? <><Loader2 size={13} className="animate-spin" /> GERANDO PDF…</> : <><FileDown size={13} /> EXPORTAR PDF ({sel.size})</>}
          </button>
        </div>
      </div>
    </div>
  );
};

// ─── Componente principal ─────────────────────────────────────────

interface Props {
  tratamentoId: string;
  clinicaId?: string;
  pacienteNome?: string;
}

export const OrthoPhotoWorkspace: React.FC<Props> = ({ tratamentoId, clinicaId, pacienteNome }) => {
  const toast = useToast();
  const confirm = useConfirm();
  const [sessoes, setSessoes] = useState<Record<string, OrtoSessao>>({});
  const [tipoAtivo, setTipoAtivo] = useState('');
  const [novoMes, setNovoMes] = useState(currentYm());
  const [loading, setLoading] = useState(true);
  const [uploading, setUploading] = useState<Record<string, boolean>>({});
  const [viewFoto, setViewFoto] = useState<{ foto: OrtoFoto; label: string } | null>(null);
  const [comparing, setComparing] = useState(false);
  const [showExport, setShowExport] = useState(false);
  const [captureSlot, setCaptureSlot] = useState<string | null>(null);

  const loadSessoes = useCallback(async () => {
    try {
      const res = await HybridBackend.getOrtoSessoes(tratamentoId);
      if (res.success) {
        const map: Record<string, OrtoSessao> = {};
        for (const s of res.sessoes) map[s.tipo] = s;
        setSessoes(map);
        // seleciona a primeira sessão (ou mantém a ativa se ainda existir)
        setTipoAtivo(prev => (prev && map[prev]) ? prev : (Object.values(map).sort((a, b) => sessionSortKey(a).localeCompare(sessionSortKey(b)))[0]?.tipo || ''));
      }
    } catch { toast.error('ERRO AO CARREGAR SESSÕES.'); }
    finally { setLoading(false); }
  }, [tratamentoId]);

  useEffect(() => { loadSessoes(); }, [loadSessoes]);

  // Garante que a sessão existe no backend (cria se necessário). Para sessões
  // mensais (tipo `mes_YYYY-MM`) deriva rótulo e data de referência.
  const ensureSessao = useCallback(async (tipo: string, rotulo?: string, dataRef?: string): Promise<OrtoSessao | null> => {
    if (sessoes[tipo]) return sessoes[tipo];
    let rot = rotulo, dr = dataRef;
    if (!rot && tipo.startsWith('mes_')) { const ym = tipo.slice(4); rot = monthLabel(ym); dr = `${ym}-01`; }
    try {
      const res = await HybridBackend.getOrtoSessao(tratamentoId, tipo, clinicaId, rot, dr);
      if (res.success) {
        setSessoes(prev => ({ ...prev, [tipo]: res.sessao }));
        return res.sessao;
      }
    } catch {}
    return null;
  }, [sessoes, tratamentoId, clinicaId]);

  const criarSessao = useCallback(async (tipo: string, rotulo?: string, dataRef?: string) => {
    const s = await ensureSessao(tipo, rotulo, dataRef);
    if (s) setTipoAtivo(s.tipo); else toast.error('ERRO AO CRIAR SESSÃO.');
  }, [ensureSessao, toast]);

  const adicionarMes = useCallback(() => {
    const ym = novoMes || currentYm();
    criarSessao(monthTipo(ym), monthLabel(ym), `${ym}-01`);
  }, [novoMes, criarSessao]);

  const handleUpload = useCallback(async (slot: string, file: File) => {
    setUploading(prev => ({ ...prev, [slot]: true }));
    try {
      let sessao = await ensureSessao(tipoAtivo);
      if (!sessao) { toast.error('ERRO AO CRIAR SESSÃO.'); return; }

      const res = await HybridBackend.uploadOrtoFoto(sessao.id, slot, file);
      if (!res.success) { toast.error('ERRO NO UPLOAD.'); return; }

      toast.success('FOTO SALVA!');
      // Atualiza state local
      setSessoes(prev => {
        const s = prev[tipoAtivo];
        if (!s) return prev;
        const fotos = s.fotos.filter(f => f.slot !== slot);
        fotos.push(res.foto!);
        return { ...prev, [tipoAtivo]: { ...s, fotos } };
      });
    } catch { toast.error('ERRO NO UPLOAD.'); }
    finally { setUploading(prev => ({ ...prev, [slot]: false })); }
  }, [tipoAtivo, ensureSessao, toast]);

  const handleDelete = useCallback(async (fotoId: string) => {
    const res = await HybridBackend.deleteOrtoFoto(fotoId);
    if (!res.success) { toast.error('ERRO AO EXCLUIR.'); return; }
    toast.success('FOTO EXCLUÍDA!');
    setSessoes(prev => {
      const updated = { ...prev };
      for (const tipo in updated) {
        updated[tipo] = { ...updated[tipo], fotos: updated[tipo].fotos.filter(f => f.id !== fotoId) };
      }
      return updated;
    });
  }, [toast]);

  const sessoesList = Object.values(sessoes).sort((a, b) => sessionSortKey(a).localeCompare(sessionSortKey(b)));
  const sessaoAtiva = sessoes[tipoAtivo] || { id: '', tipo: tipoAtivo, fotos: [] };
  const totalFotos = sessoesList.reduce((acc, s) => acc + s.fotos.length, 0);
  const especiaisDisponiveis = SPECIAL_SESSIONS.filter(sp => !sessoes[sp.tipo]);

  return (
    <div className="bg-white border border-gray-200 rounded-xl shadow-sm overflow-hidden">
      {/* Header */}
      <div className="px-5 py-4 border-b border-gray-100 bg-gray-50 flex items-center justify-between gap-3 flex-wrap">
        <div className="flex items-center gap-2">
          <Camera size={16} className="text-emerald-500" />
          <h3 className="font-black text-gray-700 uppercase text-sm">DOCUMENTAÇÃO FOTOGRÁFICA</h3>
          {totalFotos > 0 && (
            <span className="text-[10px] font-black px-2 py-0.5 bg-emerald-100 text-emerald-600 rounded-full uppercase">{totalFotos} FOTOS</span>
          )}
        </div>
        <div className="flex items-center gap-2">
          <button
            onClick={() => setShowExport(true)}
            disabled={totalFotos === 0}
            className="flex items-center gap-1.5 px-3 py-1.5 border border-gray-200 rounded-lg text-[10px] font-black text-gray-600 hover:bg-gray-100 uppercase transition-colors disabled:opacity-40"
          >
            <FileDown size={13} /> EXPORTAR
          </button>
          <button
            onClick={() => setComparing(true)}
            disabled={totalFotos === 0}
            className="flex items-center gap-1.5 px-3 py-1.5 border border-gray-200 rounded-lg text-[10px] font-black text-gray-600 hover:bg-gray-100 uppercase transition-colors disabled:opacity-40"
          >
            <SplitSquareHorizontal size={13} /> COMPARAR
          </button>
          <button onClick={loadSessoes} className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors text-gray-400">
            <RefreshCw size={14} />
          </button>
        </div>
      </div>

      {/* Adicionar sessão (orto é mensal → uma sessão por mês) */}
      <div className="px-5 py-3 border-b border-gray-100 bg-white flex flex-wrap items-center gap-2">
        <span className="text-[10px] font-black text-gray-400 uppercase mr-1">ADICIONAR SESSÃO:</span>
        <input
          type="month"
          value={novoMes}
          onChange={e => setNovoMes(e.target.value)}
          className="border border-gray-200 rounded-lg px-2 py-1 text-[11px] font-bold text-gray-700 focus:outline-none focus:border-emerald-400"
        />
        <button
          onClick={adicionarMes}
          className="flex items-center gap-1 px-2.5 py-1.5 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg text-[10px] font-black uppercase transition-colors"
        >
          <CalendarPlus size={12} /> MÊS
        </button>
        {especiaisDisponiveis.map(sp => (
          <button
            key={sp.tipo}
            onClick={() => criarSessao(sp.tipo)}
            className="flex items-center gap-1 px-2.5 py-1.5 border border-gray-200 rounded-lg text-[10px] font-black text-gray-600 hover:bg-gray-100 uppercase transition-colors"
          >
            <Plus size={12} /> {sp.label}
          </button>
        ))}
      </div>

      {/* Session tabs */}
      {sessoesList.length > 0 && (
        <div className="flex overflow-x-auto border-b border-gray-100 bg-white">
          {sessoesList.map(s => {
            const count = s.fotos.length;
            const isActive = s.tipo === tipoAtivo;
            return (
              <button
                key={s.tipo}
                onClick={() => setTipoAtivo(s.tipo)}
                className={`flex-shrink-0 px-4 py-2.5 text-[10px] font-black uppercase transition-all border-b-2 flex items-center gap-1.5 ${
                  isActive
                    ? 'border-teal-500 text-teal-600 bg-teal-50/50'
                    : 'border-transparent text-gray-500 hover:text-gray-700 hover:bg-gray-50'
                }`}
              >
                {sessionLabel(s)}
                {count > 0 && (
                  <span className={`text-[8px] font-black px-1.5 py-0.5 rounded-full ${isActive ? 'bg-teal-500 text-white' : 'bg-gray-200 text-gray-600'}`}>
                    {count}
                  </span>
                )}
              </button>
            );
          })}
        </div>
      )}

      {/* Conteúdo */}
      {loading ? (
        <div className="flex items-center justify-center h-40">
          <Loader2 size={28} className="animate-spin text-gray-300" />
        </div>
      ) : sessoesList.length === 0 ? (
        <div className="flex flex-col items-center justify-center py-12 text-gray-400 gap-2">
          <Camera size={30} />
          <p className="text-xs font-black uppercase">NENHUMA SESSÃO DE FOTOS AINDA</p>
          <p className="text-[10px] text-center max-w-xs">ADICIONE A SESSÃO DO MÊS (OU A INICIAL) ACIMA PARA COMEÇAR A DOCUMENTAÇÃO.</p>
        </div>
      ) : (
        <>
          {/* Completude da sessão ativa */}
          <div className="px-5 py-3 border-b border-gray-100 bg-gray-50/50">
            <div className="flex items-center justify-between mb-1.5">
              <p className="text-[10px] font-black text-gray-400 uppercase">COMPLETUDE — {sessionLabel(sessaoAtiva)}</p>
            </div>
            <Completude sessao={sessaoAtiva} />
          </div>

          {/* Grade */}
          <div className="p-5">
            <SlotGrid
              sessao={sessaoAtiva}
              onUpload={handleUpload}
              onView={(foto, label) => setViewFoto({ foto, label })}
              onRequestCapture={setCaptureSlot}
              uploading={uploading}
            />
          </div>

          {/* Instrução */}
          <div className="px-5 pb-4">
            <p className="text-[10px] text-gray-400 font-bold uppercase text-center">
              CLIQUE NUM SLOT VAZIO PARA ABRIR A CÂMERA (OU GALERIA) · CLIQUE NA FOTO PARA VISUALIZAR
            </p>
          </div>
        </>
      )}

      {/* Viewer */}
      {viewFoto && (
        <PhotoViewer
          foto={viewFoto.foto}
          slotLabel={viewFoto.label}
          onClose={() => setViewFoto(null)}
          onDelete={() => handleDelete(viewFoto.foto.id)}
        />
      )}

      {/* Comparador */}
      {comparing && (
        <Comparator sessoes={sessoesList} sessaoInicial={tipoAtivo} onClose={() => setComparing(false)} />
      )}

      {/* Exportação */}
      {captureSlot && (
        <CameraCapture
          title={ORTO_SLOTS.find(s => s.key === captureSlot)?.label || 'FOTO'}
          onCapture={(file) => handleUpload(captureSlot, file)}
          onClose={() => setCaptureSlot(null)}
        />
      )}

      {showExport && (
        <ExportModal sessoes={sessoesList} pacienteNome={pacienteNome} onClose={() => setShowExport(false)} />
      )}
    </div>
  );
};
