"use client";

import { useEffect, useRef, useState } from "react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { LuCloud, LuFolderOpen, LuLoader, LuPlay, LuTriangleAlert, LuCheck, LuX, LuFilePlus2 } from "react-icons/lu";
import Step4ReviewAndSave, { ManagedFile as UploadManagedFile } from "@/app/upload/components/Step4ReviewAndSave";

type Category = { id: string; name: string; icon: string | null; color: string | null };
type Folder = { label: string; path: string };
type ScannerFile = { id: string; fileName: string; extractedData: Record<string, unknown> | null; status: "EXTRACTED" | "FAILED" | "SAVED"; errorMessage: string | null; documentId: string | null };
type ScannerEvent = { id: string; type: string; message: string; context: Record<string, unknown> | null; createdAt: string };
type ScannerSession = { id: string; userId?: string; user?: { id: string; name: string | null; username: string }; folderPath: string; status: string; category: Category; files: ScannerFile[]; events?: ScannerEvent[] };
type ProcessingProgress = { phase: "searching" | "downloading" | "processing" | "finishing"; message: string; currentFile?: string; currentPage?: number; totalPages?: number; processedFiles: number; totalFiles: number; percent: number | null };
type PendingFile = { id: string; name: string };
type AvailableFile = { id: string; name: string };
const isDuplicateFile = (file: ScannerFile) => file.errorMessage?.startsWith("Esta factura ya fue guardada") ?? false;

const DEFAULT_FIELDS = "NOMBRE,CODIGO,RNC,NCF,SUB-TOTAL,ITBIS,PROPINA,TOTAL,FECHA,PRODUCTOS";

export default function SharePointScannerPage() {
  const { data: authSession, status } = useSession();
  const router = useRouter();
  const isAdmin = authSession?.user?.role === "ADMIN";
  const [folders, setFolders] = useState<Folder[]>([]);
  const [busyFolderPaths, setBusyFolderPaths] = useState<string[]>([]);
  const [afterDownload, setAfterDownload] = useState<"delete" | "keep">("delete");
  const [categories, setCategories] = useState<Category[]>([]);
  const [folderPath, setFolderPath] = useState("");
  const [categoryId, setCategoryId] = useState("");
  const [fields, setFields] = useState(DEFAULT_FIELDS);
  const [session, setSession] = useState<ScannerSession | null>(null);
  const [activeSessions, setActiveSessions] = useState<ScannerSession[]>([]);
  const [savingFileIds, setSavingFileIds] = useState<Set<string>>(new Set());
  const [busy, setBusy] = useState(false);
  const [processingProgress, setProcessingProgress] = useState<ProcessingProgress | null>(null);
  const [availableFiles, setAvailableFiles] = useState<AvailableFile[]>([]);
  const [clearConfirmation, setClearConfirmation] = useState<PendingFile[] | null>(null);
  const [message, setMessage] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const progressRunRef = useRef(0);

  // Los archivos guardados siguen disponibles en el historial, pero no deben
  // ocupar espacio en la bandeja de revisión de la sesión activa.
  const onlyPendingFiles = (value: ScannerSession): ScannerSession => ({
    ...value,
    files: value.files.filter((file) => file.status !== "SAVED"),
  });

  const trackSession = (value: ScannerSession | null) => {
    setSession(value);
    if (value) setActiveSessions((previous) => previous.map((item) => item.id === value.id ? { ...item, ...value, user: value.user ?? item.user } : item));
  };

  const selectActiveSession = (value: ScannerSession) => {
    const selected = onlyPendingFiles(value);
    setSession(selected);
    setAvailableFiles([]);
    setError(null);
    setProcessingProgress(value.status === "PROCESSING" ? progressFromSession(value) ?? { phase: "searching", message: "Procesando documentos en SharePoint…", processedFiles: 0, totalFiles: 0, percent: null } : null);
  };

  const refreshAvailableFiles = async (sessionId: string) => {
    try {
      const response = await fetch(`/api/scanner/sharepoint/sessions/${sessionId}/available`, { cache: "no-store" });
      const data = await response.json();
      if (response.ok && Array.isArray(data.files)) setAvailableFiles(data.files);
    } catch {
      // La comprobación es informativa y no debe interrumpir la sesión.
    }
  };

  const progressFromSession = (value: ScannerSession): ProcessingProgress | null => {
    const event = value.events?.[0];
    if (!event) return null;
    const context = event.context || {};
    const rawPhase = typeof context.phase === "string" ? context.phase : "processing";
    const phase: ProcessingProgress["phase"] = rawPhase === "searching" ? "searching" : rawPhase === "downloading" ? "downloading" : rawPhase === "finished" ? "finishing" : "processing";
    const totalFiles = typeof context.totalFiles === "number" ? context.totalFiles : 0;
    const processedFiles = typeof context.processedFiles === "number" ? context.processedFiles : 0;
    const totalPages = typeof context.totalPages === "number" ? context.totalPages : undefined;
    const currentPage = typeof context.currentPage === "number" ? context.currentPage : undefined;
    const currentFile = typeof context.currentFile === "string" ? context.currentFile : undefined;
    let percent: number | null = null;
    if (totalFiles > 0) {
      const pageProgress = totalPages && currentPage ? Math.max(0, (currentPage - 1) / totalPages) : 0;
      percent = rawPhase === "finished" ? 100 : Math.min(99, Math.round(((processedFiles + pageProgress) / totalFiles) * 100));
    } else if (rawPhase === "finished" || rawPhase === "files_found") {
      percent = 100;
    }
    return { phase, message: event.message, currentFile, currentPage, totalPages, processedFiles, totalFiles, percent };
  };

  const refreshProcessingProgress = async (sessionId: string, runId = progressRunRef.current) => {
    try {
      const response = await fetch(`/api/scanner/sharepoint/sessions/${sessionId}`, { cache: "no-store" });
      const data = await response.json();
      if (response.ok && data.session) {
        const progress = progressFromSession(data.session);
        if (progress && runId === progressRunRef.current) setProcessingProgress(progress);
      }
    } catch {
      // La petición principal continúa; un fallo aislado del indicador no debe interrumpir el proceso.
    }
  };

  useEffect(() => {
    if (status !== "authenticated") return;
    Promise.all([fetch("/api/scanner/sharepoint/sessions"), fetch("/api/categories")])
      .then(async ([scannerResponse, categoryResponse]) => {
        const scannerData = await scannerResponse.json();
        const categoryData = await categoryResponse.json();
        if (!scannerResponse.ok) throw new Error(scannerData.error || "No se pudo cargar SharePoint.");
        if (!scannerData.enabled) throw new Error("SharePoint está deshabilitado. Configura SHAREPOINT_ENABLED=true.");
        setFolders(scannerData.folders);
        const occupiedPaths = (scannerData.busyFolderPaths || []).map((path: string) => path.toLowerCase());
        setBusyFolderPaths(occupiedPaths);
        setAfterDownload(scannerData.afterDownload === "keep" ? "keep" : "delete");
        setFolderPath(scannerData.folders.find((folder: Folder) => !occupiedPaths.includes(folder.path.toLowerCase()))?.path || "");
        setCategories(categoryData.categories || []);
        if (categoryData.categories?.[0]) setCategoryId(categoryData.categories[0].id);
        const loadedSessions = (scannerData.activeSessions || (scannerData.activeSession ? [scannerData.activeSession] : [])).map((item: ScannerSession) => onlyPendingFiles(item));
        setActiveSessions(loadedSessions);
        const selectedSession = scannerData.activeSession || (isAdmin ? loadedSessions[0] : null);
        if (selectedSession) {
          selectActiveSession(selectedSession);
          void refreshAvailableFiles(selectedSession.id);
        }
      })
      .catch((loadError: unknown) => setError(loadError instanceof Error ? loadError.message : "No se pudo cargar la configuración."));
  }, [status]);

  useEffect(() => {
    if (status !== "authenticated" || busy || !session || session.status === "PROCESSING") return;
    void refreshAvailableFiles(session.id);
    const interval = window.setInterval(() => void refreshAvailableFiles(session.id), 15000);
    return () => window.clearInterval(interval);
  }, [status, busy, session?.id, session?.status]);

  useEffect(() => {
    if (status !== "authenticated" || busy || !session || session.status !== "PROCESSING") return;
    let cancelled = false;
    const poll = async () => {
      try {
        const response = await fetch(`/api/scanner/sharepoint/sessions/${session.id}`, { cache: "no-store" });
        const data = await response.json();
        if (cancelled || !response.ok || !data.session) return;
        if (data.session.status === "PROCESSING") {
          const progress = progressFromSession(data.session);
          if (progress) setProcessingProgress(progress);
          trackSession({ ...onlyPendingFiles(data.session), status: "PROCESSING" });
        } else {
          trackSession(onlyPendingFiles(data.session));
          setProcessingProgress(null);
        }
      } catch {
        // El siguiente ciclo de consulta reintentará actualizar el estado.
      }
    };
    const interval = window.setInterval(() => void poll(), 800);
    void poll();
    return () => { cancelled = true; window.clearInterval(interval); };
  }, [status, busy, session?.id, session?.status]);

  const createSession = async (confirmClear = false) => {
    setBusy(true); setError(null); setMessage(null);
    try {
      const response = await fetch("/api/scanner/sharepoint/sessions", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ folderPath, categoryId, customFields: fields, confirmClear }) });
      const data = await response.json();
      if (response.status === 409 && data.confirmationRequired) {
        setClearConfirmation(data.pendingFiles as PendingFile[]);
        return;
      }
      if (!response.ok) throw new Error(data.error || "No se pudo crear la sesión.");
      const created = { ...data.session, files: [] } as ScannerSession;
      setSession(created);
      setActiveSessions((previous) => [created, ...previous.filter((item) => item.id !== created.id)]);
      window.dispatchEvent(new CustomEvent("scanner-session-changed"));
      setMessage(data.clearedFiles.length ? `Sesión iniciada. Se eliminaron ${data.clearedFiles.length} documento(s) previos.` : "Sesión limpia iniciada. Ya puedes procesar los documentos que lleguen a la carpeta.");
    } catch (requestError) { setError(requestError instanceof Error ? requestError.message : "No se pudo crear la sesión."); }
    finally { setBusy(false); }
  };

  const confirmClearAndCreateSession = async () => {
    setClearConfirmation(null);
    await createSession(true);
  };

  const processSession = async () => {
    if (!session) return;
    setBusy(true); setError(null); setMessage(null); setProcessingProgress({ phase: "searching", message: "Buscando documentos en SharePoint…", processedFiles: 0, totalFiles: 0, percent: null });
    setAvailableFiles([]);
    trackSession(session ? { ...session, status: "PROCESSING" } : null);
    const progressRunId = progressRunRef.current + 1;
    progressRunRef.current = progressRunId;
    const progressInterval = window.setInterval(() => void refreshProcessingProgress(session.id, progressRunId), 800);
    void refreshProcessingProgress(session.id, progressRunId);
    try {
      const response = await fetch(`/api/scanner/sharepoint/sessions/${session.id}/process`, { method: "POST" });
      const data = await response.json();
      if (!response.ok) throw new Error(data.error || "No se pudo procesar la sesión.");
      trackSession(onlyPendingFiles(data.session));
      const extractedPages = data.summary?.extractedPages ?? data.result.filter((item: { status: string }) => item.status === "extracted").length;
      const failedPages = data.summary?.failedPages ?? data.result.filter((item: { status: string }) => item.status === "failed").length;
      const failedSourceFiles = data.summary?.failedSourceFiles ?? failedPages;
      const remoteAction = afterDownload === "delete" ? "y eliminada(s) de SharePoint" : "y conservada(s) en SharePoint";
      setMessage(`Procesamiento terminado: ${extractedPages} página(s) extraída(s) ${remoteAction}.${failedPages ? ` ${failedPages} página(s) fallaron; ${failedSourceFiles} archivo(s) original(es) permanecieron en SharePoint.` : ""}`);
    } catch (requestError) {
      trackSession(session ? { ...session, status: "FAILED" } : null);
      setError(requestError instanceof Error ? requestError.message : "No se pudo procesar la sesión.");
    }
    finally { window.clearInterval(progressInterval); progressRunRef.current += 1; setBusy(false); setProcessingProgress(null); }
  };

  const closeSession = async () => {
    if (!session || !window.confirm("¿Cerrar esta sesión? Los documentos pendientes en SharePoint no se eliminarán.")) return;
    setBusy(true); setError(null);
    try {
      const response = await fetch(`/api/scanner/sharepoint/sessions/${session.id}/close`, { method: "POST" });
      const data = await response.json();
      if (!response.ok) throw new Error(data.error || "No se pudo cerrar la sesión.");
      setActiveSessions((previous) => previous.filter((item) => item.id !== session.id));
      setSession(null);
      window.dispatchEvent(new CustomEvent("scanner-session-changed"));
      setMessage("Sesión cerrada. Puedes abrir una nueva sesión cuando lo necesites.");
    } catch (requestError) { setError(requestError instanceof Error ? requestError.message : "No se pudo cerrar la sesión."); }
    finally { setBusy(false); }
  };

  const changeValue = (fileId: string, key: string, value: string) => setSession((previous) => previous ? { ...previous, files: previous.files.map((file) => file.id === fileId ? { ...file, extractedData: { ...(file.extractedData || {}), [key]: value } } : file) } : previous);
  const saveFile = async (file: ScannerFile) => {
    if (!session || !file.extractedData) return;
    setBusy(true); setError(null); setSavingFileIds((previous) => new Set(previous).add(file.id));
    let duplicateRejected = false;
    try {
      const response = await fetch(`/api/scanner/sharepoint/sessions/${session.id}/save`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ fileId: file.id, extractedData: file.extractedData }) });
      const data = await response.json();
      duplicateRejected = response.status === 409 && Boolean(data.duplicateDocument);
      if (!response.ok) throw new Error(data.error || "No se pudo guardar el documento.");
      setSession((previous) => previous ? { ...previous, files: previous.files.filter((item) => item.id !== file.id) } : previous);
      setActiveSessions((previous) => previous.map((item) => item.id === session.id ? { ...item, files: item.files.filter((entry) => entry.id !== file.id) } : item));
    } catch (requestError) {
      const message = requestError instanceof Error ? requestError.message : "No se pudo guardar el documento.";
      if (duplicateRejected) {
        setSession((previous) => previous ? { ...previous, files: previous.files.map((item) => item.id === file.id ? { ...item, errorMessage: message } : item) } : previous);
        setActiveSessions((previous) => previous.map((item) => item.id === session.id ? { ...item, files: item.files.map((entry) => entry.id === file.id ? { ...entry, errorMessage: message } : entry) } : item));
      }
      setError(message);
    }
    finally {
      setBusy(false);
      setSavingFileIds((previous) => { const next = new Set(previous); next.delete(file.id); return next; });
    }
  };

  const saveFileById = (fileId: string) => {
    const file = session?.files.find((item) => item.id === fileId);
    if (file) void saveFile(file);
  };

  const saveAllFiles = async () => {
    if (!session) return;
    for (const file of session.files.filter((item) => item.status === "EXTRACTED")) await saveFile(file);
  };

  const cancelAllFiles = async () => {
    if (!session) return;
    const unsavedCount = session.files.filter((file) => file.status !== "SAVED").length;
    if (!unsavedCount || !window.confirm(`¿Cancelar los ${unsavedCount} resultado(s) sin guardar? Se eliminarán los archivos temporales y no se crearán documentos. Los archivos ya eliminados de SharePoint no se recuperan.`)) return;
    setBusy(true); setError(null); setMessage(null);
    try {
      const response = await fetch(`/api/scanner/sharepoint/sessions/${session.id}/discard`, { method: "POST" });
      const data = await response.json();
      if (!response.ok) throw new Error(data.error || "No se pudieron cancelar los resultados.");
      trackSession(onlyPendingFiles(data.session));
      setMessage(`Se cancelaron ${data.discardedCount} resultado(s) sin guardar.`);
    } catch (requestError) { setError(requestError instanceof Error ? requestError.message : "No se pudieron cancelar los resultados."); }
    finally { setBusy(false); }
  };

  const cancelDuplicateFiles = async () => {
    if (!session) return;
    const duplicateCount = session.files.filter(isDuplicateFile).length;
    if (!duplicateCount || !window.confirm(`¿Cancelar las ${duplicateCount} factura(s) duplicada(s)? Se eliminarán únicamente de esta revisión.`)) return;
    setBusy(true); setError(null); setMessage(null);
    try {
      const response = await fetch(`/api/scanner/sharepoint/sessions/${session.id}/discard`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ duplicatesOnly: true }) });
      const data = await response.json();
      if (!response.ok) throw new Error(data.error || "No se pudieron cancelar las duplicadas.");
      trackSession(onlyPendingFiles(data.session));
      setMessage(`Se cancelaron ${data.discardedCount} factura(s) duplicada(s).`);
    } catch (requestError) { setError(requestError instanceof Error ? requestError.message : "No se pudieron cancelar las duplicadas."); }
    finally { setBusy(false); }
  };

  const reviewFiles: UploadManagedFile[] = (session?.files ?? []).map((file) => ({
    id: file.id,
    file: new File([], file.fileName),
    status: savingFileIds.has(file.id) ? "saving" : file.status === "EXTRACTED" ? (file.errorMessage ? "error" : "completed") : file.status === "SAVED" ? "saved" : "error",
    progress: 100,
    extractedData: file.extractedData as UploadManagedFile["extractedData"],
    errorMessage: file.errorMessage ?? undefined,
    savedId: file.documentId ?? undefined,
  }));
  const sessionProcessing = session?.status === "PROCESSING";

  if (status === "unauthenticated") { router.replace("/login"); return null; }
  if (status === "loading") return <p className="p-8 text-center">Cargando…</p>;

  return <div className="max-w-5xl mx-auto space-y-6">
    <header><h1 className="text-2xl md:text-3xl font-bold text-text flex items-center gap-3"><LuCloud className="text-primary" /> Escáner SharePoint</h1></header>
    {error && <div className="p-4 rounded-xl bg-destructive-background border border-destructive-border text-destructive-foreground flex gap-2"><LuTriangleAlert className="mt-0.5" />{error}</div>}
    {message && <div className="p-4 rounded-xl bg-success-background border border-success-border text-success-foreground flex gap-2"><LuCheck className="mt-0.5" />{message}</div>}
    {processingProgress && <section className="bg-background border border-primary-subtle rounded-2xl p-5 shadow-sm" role="status" aria-live="polite">
      <div className="flex items-start justify-between gap-4 mb-3"><div><p className="text-sm font-semibold text-text">Procesando documentos</p><p className="text-sm text-text-muted mt-1">{processingProgress.message}</p></div><LuLoader className="text-primary animate-spin shrink-0" aria-hidden="true" /></div>
      <div className="h-2.5 rounded-full bg-primary-subtle overflow-hidden" aria-label={processingProgress.percent === null ? "Progreso en curso" : `${processingProgress.percent}% completado`}>
        <div className={`h-full rounded-full bg-primary transition-all duration-500 ${processingProgress.percent === null ? "scanner-progress-indeterminate" : ""}`} style={{ width: `${processingProgress.percent ?? 35}%` }} />
      </div>
      <div className="flex items-center justify-between gap-3 mt-2 text-xs text-text-muted"><span>{processingProgress.totalFiles > 0 ? `${processingProgress.processedFiles} de ${processingProgress.totalFiles} archivo(s) terminado(s)` : "Preparando búsqueda…"}</span><span>{processingProgress.percent === null ? "En curso" : `${processingProgress.percent}%`}</span></div>
      {processingProgress.currentFile && <p className="text-xs text-text-muted mt-2 truncate" title={processingProgress.currentFile}>Archivo actual: {processingProgress.currentFile}{processingProgress.totalPages && processingProgress.currentPage ? ` · página ${processingProgress.currentPage} de ${processingProgress.totalPages}` : ""}</p>}
      <div className="grid grid-cols-3 gap-2 mt-4 text-xs"><div className={`rounded-lg px-3 py-2 ${processingProgress.phase === "searching" ? "bg-primary-faint text-primary font-semibold" : "bg-background-alt text-text-muted"}`}>1. Buscar archivos</div><div className={`rounded-lg px-3 py-2 ${processingProgress.phase === "downloading" ? "bg-primary-faint text-primary font-semibold" : "bg-background-alt text-text-muted"}`}>2. Descargar</div><div className={`rounded-lg px-3 py-2 ${processingProgress.phase === "processing" || processingProgress.phase === "finishing" ? "bg-primary-faint text-primary font-semibold" : "bg-background-alt text-text-muted"}`}>3. Procesar y guardar</div></div>
    </section>}
    {clearConfirmation && <div className="fixed inset-0 z-50 flex items-center justify-center bg-overlay px-4" role="presentation" onMouseDown={(event) => { if (event.target === event.currentTarget) setClearConfirmation(null); }}>
      <div className="w-full max-w-lg rounded-2xl bg-background border border-border-subtle shadow-2xl" role="dialog" aria-modal="true" aria-labelledby="clear-session-title">
        <div className="flex items-start gap-3 p-6 border-b border-border-subtle"><div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-warning-background text-warning-foreground"><LuTriangleAlert /></div><div className="min-w-0"><h2 id="clear-session-title" className="text-lg font-bold text-text">Documentos encontrados</h2><p className="text-sm text-text-muted mt-1">La carpeta contiene {clearConfirmation.length} documento(s) pendiente(s).</p></div><button type="button" onClick={() => setClearConfirmation(null)} aria-label="Cerrar" className="ml-auto rounded-lg p-2 text-text-muted hover:bg-background-alt"><LuX /></button></div>
        <div className="p-6 space-y-4"><p className="text-sm text-text-muted">Al abrir una sesión nueva se eliminarán permanentemente de SharePoint:</p><div className="max-h-32 overflow-y-auto rounded-xl border border-border-subtle bg-background-alt p-3 space-y-2">{clearConfirmation.slice(0, 5).map((file) => <p key={file.id} className="text-sm text-text truncate" title={file.name}>{file.name}</p>)}{clearConfirmation.length > 5 && <p className="text-xs text-text-muted">Y {clearConfirmation.length - 5} documento(s) más…</p>}</div><p className="text-sm font-semibold text-warning-foreground">Esta acción no se puede deshacer. ¿Deseas continuar?</p></div>
        <div className="flex flex-col-reverse sm:flex-row sm:justify-end gap-3 p-6 pt-0"><button type="button" onClick={() => setClearConfirmation(null)} className="px-4 py-2.5 rounded-xl border border-border-subtle text-text font-semibold hover:bg-background-alt">Cancelar</button><button type="button" onClick={() => void confirmClearAndCreateSession()} className="px-4 py-2.5 rounded-xl bg-destructive text-primary-foreground font-semibold hover:bg-destructive-hover">Continuar y eliminar</button></div>
      </div>
    </div>}

    {isAdmin && activeSessions.length > 0 && <section className="bg-background border border-border-subtle rounded-2xl p-5 space-y-4">
      <div className="flex flex-wrap items-start justify-between gap-3"><div><h2 className="font-bold text-lg text-text">Sesiones activas</h2><p className="text-sm text-text-muted mt-1">Como administrador puedes supervisar y operar la sesión de cualquier usuario.</p></div><button type="button" onClick={() => { setSession(null); setProcessingProgress(null); }} disabled={busy} className="px-3 py-2 rounded-lg border border-border-subtle text-sm font-semibold text-text hover:bg-background-alt disabled:opacity-50">Abrir nueva sesión</button></div>
      <div className="grid gap-3 md:grid-cols-2">
        {activeSessions.map((active) => {
          const owner = active.user?.name || active.user?.username || "Usuario";
          const selected = session?.id === active.id;
          const statusLabel = active.status === "PROCESSING" ? "Procesando" : active.status === "FAILED" ? "Con errores" : "Activa";
          return <button key={active.id} type="button" onClick={() => selectActiveSession(active)} disabled={busy} className={`text-left rounded-xl border p-4 transition-colors disabled:opacity-60 ${selected ? "border-primary bg-primary-faint" : "border-border-subtle hover:bg-background-alt"}`}>
            <div className="flex items-center justify-between gap-3"><span className="font-semibold text-text truncate">{owner}</span><span className={`text-xs font-semibold ${active.status === "FAILED" ? "text-destructive-foreground" : "text-primary"}`}>{statusLabel}</span></div>
            <p className="text-sm text-text mt-1 truncate" title={active.folderPath}>{active.folderPath}</p>
            <p className="text-xs text-text-muted mt-1">{active.category.icon} {active.category.name}</p>
          </button>;
        })}
      </div>
    </section>}
    {!session ? <section className="bg-background border border-border-subtle rounded-2xl p-6 space-y-5">
      <h2 className="font-bold text-lg">Abrir sesión de escaneo</h2>
      <div className="grid md:grid-cols-2 gap-5">
        <label className="text-sm font-semibold">Carpeta de SharePoint<select value={folderPath} onChange={(event) => setFolderPath(event.target.value)} className="mt-2 w-full px-3 py-2.5 rounded-lg border border-border-subtle bg-background"><option value="">Selecciona una carpeta</option>{folders.map((folder) => { const inUse = busyFolderPaths.includes(folder.path.toLowerCase()); return <option key={folder.path} value={folder.path} disabled={inUse}>{folder.label} — {folder.path}{inUse ? " (en uso)" : ""}</option>; })}</select>{busyFolderPaths.includes(folderPath.toLowerCase()) && <span className="block mt-1 text-xs text-destructive-foreground">Esta carpeta está siendo utilizada por otra sesión activa.</span>}</label>
        <label className="text-sm font-semibold">Categoría<select value={categoryId} onChange={(event) => setCategoryId(event.target.value)} className="mt-2 w-full px-3 py-2.5 rounded-lg border border-border-subtle bg-background"><option value="">Selecciona una categoría</option>{categories.map((category) => <option key={category.id} value={category.id}>{category.icon} {category.name}</option>)}</select></label>
      </div>
      <label className="block text-sm font-semibold">Campos a extraer<textarea value={fields} onChange={(event) => setFields(event.target.value)} rows={3} className="mt-2 w-full px-3 py-2.5 rounded-lg border border-border-subtle bg-background font-normal" /></label>
      <p className="text-xs text-text-muted">Antes de abrir, se comprobarán los documentos de la carpeta. Si existen, deberás confirmar su eliminación para iniciar una sesión limpia.</p>
      <button onClick={() => createSession()} disabled={busy || !folderPath || !categoryId || !fields.trim()} className="px-5 py-3 rounded-xl bg-primary text-primary-foreground font-semibold disabled:opacity-50 flex items-center gap-2"><LuFolderOpen />{busy ? "Verificando…" : "Abrir sesión"}</button>
    </section> : <>
      <section className="bg-background border border-border-subtle rounded-2xl p-6 flex flex-col md:flex-row md:items-center gap-4 justify-between"><div><p className="text-xs text-text-muted">Sesión activa · {session.category.name}</p><h2 className="font-bold text-lg">{session.folderPath}</h2><p className="text-sm text-text-muted mt-1">{afterDownload === "delete" ? "Los archivos se eliminan de SharePoint solo después de una extracción válida." : "Los archivos se conservan en SharePoint después de una extracción válida."}</p>{availableFiles.length > 0 && <div className="mt-3 inline-flex items-center gap-2 rounded-lg border border-primary-subtle bg-primary-faint px-3 py-2 text-sm font-semibold text-primary" role="status" aria-live="polite"><LuFilePlus2 className="shrink-0" />Hay {availableFiles.length} documento(s) disponible(s) para procesar.</div>}</div><div className="flex flex-wrap gap-3"><button onClick={closeSession} disabled={busy || sessionProcessing} className="px-4 py-3 rounded-xl border border-border-subtle text-text font-semibold disabled:opacity-50 flex items-center gap-2 hover:bg-background-alt"><LuX />Cerrar sesión</button><button onClick={processSession} disabled={busy || sessionProcessing} className="px-5 py-3 rounded-xl bg-primary text-primary-foreground font-semibold disabled:opacity-50 flex items-center gap-2"><LuPlay />{busy || sessionProcessing ? "Procesando…" : "Buscar y procesar documentos"}</button></div></section>
      {session.files.length === 0 ? <div className="p-8 rounded-2xl border border-dashed border-border-subtle text-center text-text-muted">Aún no hay resultados. Coloca documentos en la carpeta y pulsa procesar.</div> : <Step4ReviewAndSave files={reviewFiles} onReset={() => void closeSession()} onDataChange={changeValue} onSaveData={saveFileById} onSaveAll={() => void saveAllFiles()} onCancelAll={() => void cancelAllFiles()} onCancelDuplicates={() => void cancelDuplicateFiles()} duplicateCount={session.files.filter(isDuplicateFile).length} selectedCategory={session.category.id} categories={[session.category]} />}
    </>}
  </div>;
}
