// ─────────────────────────────────────────────────────
// GUT-225 — Explorador de archivos tipo escritorio.
// Presenta la documentación de una entidad (hoy: el expediente) como un
// explorador de carpetas al estilo del Explorador de Windows: panel de
// navegación, barra de direcciones con ruta, vista de detalles o iconos,
// doble clic para abrir, menú contextual y barra de estado.
//
// Las carpetas son las secciones documentales de GUT-68 (`archivos.categoria`),
// las mismas que existen en el volumen. No hay carpetas nuevas ni anidadas:
// una sola petición trae los archivos de la entidad y se agrupan en cliente.
//
// Lo consume ExpedienteDocumentos vía window.ExploradorArchivos.
//
// INC-004 — dos props opcionales para encadenar exploradores (nivel Cliente):
//   parentCrumbs: [{ label, onClick }] niveles por encima de la raíz; se pintan
//     antes de `rootLabel` en la barra de direcciones y en el panel, y
//     «Atrás»/«Subir un nivel» desde la raíz van al último.
//   subcarpetas: [{ key, label, sub, count, onOpen }] carpetas de la raíz que
//     no son una `categoria` de esta entidad sino otro explorador (p. ej. un
//     expediente dentro del cliente). Se listan antes de `carpetas` y al
//     abrirlas se llama a onOpen.
// ─────────────────────────────────────────────────────

// Cajón para los archivos cuya `categoria` es nula o no está en la lista de
// secciones (subidos antes de GUT-68). Solo se muestra si tiene contenido.
const FX_SIN_CLASIFICAR = 'Sin clasificar';

const fxExt = (f) => {
  const n = String(f.original_name || '');
  const i = n.lastIndexOf('.');
  const e = i > 0 ? n.slice(i + 1) : '';
  return e ? e.toUpperCase().slice(0, 4) : 'ARCH';
};

const FX_CAD_EXT = new Set(['dwg', 'dxf', 'dwf']);
const FX_MODELO3D_EXT = new Set(['step', 'stp', 'iges', 'igs', '3dm', 'skp']);

const fxTipo = (f) => {
  const m = String(f.mimetype || '');
  const ext = fxExt(f).toLowerCase();
  if (m.startsWith('image/') && !FX_CAD_EXT.has(ext)) return 'Imagen';
  if (m === 'application/pdf') return 'Documento PDF';
  if (m.indexOf('spreadsheet') >= 0 || m.indexOf('excel') >= 0 || ext === 'csv') return 'Hoja de cálculo';
  if (m.indexOf('word') >= 0) return 'Documento Word';
  if (m.indexOf('presentation') >= 0 || m.indexOf('powerpoint') >= 0) return 'Presentación';
  if (FX_CAD_EXT.has(ext)) return 'Plano CAD';
  if (FX_MODELO3D_EXT.has(ext)) return 'Modelo 3D';
  if (ext === 'txt') return 'Texto';
  if (ext === 'zip') return 'ZIP';
  return `Archivo ${fxExt(f)}`;
};

const fxFecha = (v) => {
  if (!v) return '—';
  try {
    const d = new Date(v);
    return `${d.toLocaleDateString('es-ES')} ${d.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })}`;
  } catch (_) {
    return '—';
  }
};

// Miniatura: imagen real si el archivo lo es, si no la extensión.
const FxThumb = ({ file, size }) => {
  const [src, setSrc] = React.useState('');
  const esImagen = archivoIsImage(file);

  React.useEffect(() => {
    if (!esImagen) { setSrc(''); return; }
    let url = '';
    let alive = true;
    API.request(`/api/archivos/${file.id}/download`).then(async res => {
      if (!res.ok) return;
      const blob = await res.blob();
      if (!alive) { return; }
      url = URL.createObjectURL(blob);
      setSrc(url);
    }).catch(() => {});
    return () => {
      alive = false;
      if (url) URL.revokeObjectURL(url);
    };
  }, [file.id, esImagen]);

  if (esImagen && src) return <img src={src} alt=""/>;
  if (esImagen) return <Icon name="file" size={size}/>;
  return <span className="fx-ext">{fxExt(file)}</span>;
};

const FX_SIN_PADRES = [];

const ExploradorArchivos = ({ entityType, entityId, carpetas, rootLabel = 'Documentos', onToast, parentCrumbs = FX_SIN_PADRES, subcarpetas = FX_SIN_PADRES }) => {
  const [files, setFiles] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');
  const [carpeta, setCarpeta] = React.useState(null); // null = raíz
  const [sel, setSel] = React.useState(null);         // id de archivo seleccionado
  const [q, setQ] = React.useState('');
  const [vista, setVista] = React.useState(() => localStorage.getItem('erp_explorador_vista') || 'detalles');
  const [uploading, setUploading] = React.useState(false);
  const [dragging, setDragging] = React.useState(false);
  const [menu, setMenu] = React.useState(null);       // {x, y, file}
  const inputRef = React.useRef(null);

  const toast = (msg, isErr) => { if (onToast) onToast(msg, isErr); };

  const load = React.useCallback(() => {
    if (!entityType || !entityId) return;
    setLoading(true);
    setError('');
    API.get('/api/archivos', { entity_type: entityType, entity_id: entityId })
      .then(rows => setFiles(Array.isArray(rows) ? rows : []))
      .catch(e => { setError(e.message || 'Error al cargar archivos'); setFiles([]); })
      .finally(() => setLoading(false));
  }, [entityType, entityId]);

  React.useEffect(() => { load(); }, [load]);

  React.useEffect(() => { localStorage.setItem('erp_explorador_vista', vista); }, [vista]);

  // Cerrar el menú contextual al hacer clic/scroll fuera.
  React.useEffect(() => {
    if (!menu) return;
    const close = () => setMenu(null);
    document.addEventListener('click', close);
    document.addEventListener('scroll', close, true);
    return () => {
      document.removeEventListener('click', close);
      document.removeEventListener('scroll', close, true);
    };
  }, [menu]);

  const carpetaDe = (f) => {
    const c = (f.categoria || '').trim();
    return c && carpetas.indexOf(c) >= 0 ? c : FX_SIN_CLASIFICAR;
  };

  const porCarpeta = React.useMemo(() => {
    const map = {};
    carpetas.forEach(c => { map[c] = []; });
    files.forEach(f => {
      const c = carpetaDe(f);
      (map[c] = map[c] || []).push(f);
    });
    return map;
  }, [files, carpetas]);

  // La carpeta cajón solo existe si tiene archivos dentro.
  const visibles = React.useMemo(
    () => carpetas.concat((porCarpeta[FX_SIN_CLASIFICAR] || []).length ? [FX_SIN_CLASIFICAR] : []),
    [carpetas, porCarpeta]
  );

  const buscando = q.trim() !== '';
  const filtra = (arr) => {
    const t = q.trim().toLowerCase();
    return t ? arr.filter(f => String(f.original_name || '').toLowerCase().indexOf(t) >= 0) : arr;
  };

  // En la raíz la búsqueda mira todas las carpetas (como el Explorador);
  // dentro de una carpeta, filtra solo esa.
  // En la raíz no hay archivos sueltos: todo cuelga de una carpeta.
  const enCarpeta = carpeta ? (porCarpeta[carpeta] || []) : [];
  const listado = buscando ? filtra(carpeta ? enCarpeta : files) : enCarpeta;
  const mostrarCarpetas = !carpeta && !buscando;

  const seleccionado = listado.find(f => f.id === sel) || null;

  const abrirCarpeta = (c) => { setCarpeta(c); setSel(null); setQ(''); };
  const subir = () => { setCarpeta(null); setSel(null); setQ(''); };
  // INC-004: desde la raíz, «Atrás»/«Subir un nivel» van al nivel padre.
  const padre = parentCrumbs.length ? parentCrumbs[parentCrumbs.length - 1] : null;
  const subirNivel = () => {
    if (carpeta || buscando || !padre) subir();
    else padre.onClick();
  };
  const mostrarSub = mostrarCarpetas ? subcarpetas : FX_SIN_PADRES;
  const nSub = (s) => Number(s.count || 0);

  const abrirArchivo = async (f) => {
    // GUT-232: la pestaña se abre síncrona (antes del await) para que el
    // navegador no la bloquee como popup; luego se le asigna la URL firmada.
    const w = window.open('', '_blank');
    try {
      const { url } = await API.post(`/api/archivos/${f.id}/link`);
      if (w) w.location = url;
    } catch (e) {
      if (w) w.close();
      toast(e.message || 'Error al abrir archivo', true);
    }
  };

  const descargar = (f) =>
    API.download(`/api/archivos/${f.id}/download`, f.original_name || `archivo_${f.id}`)
      .catch(e => toast(e.message || 'Error al descargar', true));

  const eliminar = async (f) => {
    if (!confirm(`¿Eliminar «${f.original_name || 'este archivo'}»?`)) return;
    try {
      await API.del(`/api/archivos/${f.id}`);
      setFiles(rows => rows.filter(x => x.id !== f.id));
      setSel(null);
      toast('Archivo eliminado');
    } catch (e) {
      toast(e.message || 'Error al eliminar archivo', true);
    }
  };

  const subirArchivos = async (lista) => {
    const selected = Array.from(lista || []);
    if (!selected.length) return;
    if (!carpeta || carpeta === FX_SIN_CLASIFICAR) {
      toast('Entra en una carpeta para subir archivos', true);
      return;
    }
    if (selected.length > 5) { toast('Máximo 5 archivos a la vez', true); return; }
    const fd = new FormData();
    fd.append('entity_type', entityType);
    fd.append('entity_id', String(entityId));
    fd.append('categoria', carpeta);
    selected.forEach(f => fd.append('files', f));
    setUploading(true);
    try {
      await API.upload('/api/archivos', fd);
      toast(`${selected.length} archivo${selected.length === 1 ? '' : 's'} subido${selected.length === 1 ? '' : 's'} ✓`);
      load();
    } catch (e) {
      toast(e.message || 'Error al subir archivo', true);
    } finally {
      setUploading(false);
      if (inputRef.current) inputRef.current.value = '';
    }
  };

  const onDrop = (ev) => {
    ev.preventDefault();
    setDragging(false);
    subirArchivos(ev.dataTransfer && ev.dataTransfer.files);
  };

  const onKeyDown = (ev) => {
    if (ev.key === 'Backspace' && (carpeta || padre)) { ev.preventDefault(); subirNivel(); return; }
    if (!seleccionado) return;
    if (ev.key === 'Enter') { ev.preventDefault(); abrirArchivo(seleccionado); }
    else if (ev.key === 'Delete') { ev.preventDefault(); eliminar(seleccionado); }
  };

  const totalSel = seleccionado ? Number(seleccionado.size || 0) : 0;
  const nElementos = (mostrarCarpetas ? visibles.length : 0) + mostrarSub.length + listado.length;

  const puedeSubir = !!carpeta && carpeta !== FX_SIN_CLASIFICAR;

  return (
    <div className="fx">
      {/* Barra de herramientas + dirección */}
      <div className="fx-bar">
        <button className="fx-nav tip" data-tip="Atrás" onClick={subirNivel} disabled={!carpeta && !buscando && !padre}>
          <Icon name="chevron_left" size={14}/>
        </button>
        <button className="fx-nav tip" data-tip="Subir un nivel" onClick={subirNivel} disabled={!carpeta && !padre}>
          <Icon name="arrow_up" size={14}/>
        </button>

        <div className="fx-path">
          {parentCrumbs.map((pc, i) => (
            <React.Fragment key={`pc-${i}`}>
              <button className="fx-crumb" onClick={pc.onClick}>
                {i === 0 && <Icon name="folder" size={13}/>} {pc.label}
              </button>
              <Icon name="chevron_right" size={12} className="fx-sep"/>
            </React.Fragment>
          ))}
          <button className="fx-crumb" onClick={subir}>
            <Icon name="folder" size={13}/> {rootLabel}
          </button>
          {carpeta && (
            <>
              <Icon name="chevron_right" size={12} className="fx-sep"/>
              <span className="fx-crumb current">{carpeta}</span>
            </>
          )}
          {buscando && (
            <>
              <Icon name="chevron_right" size={12} className="fx-sep"/>
              <span className="fx-crumb current">Resultados de «{q.trim()}»</span>
            </>
          )}
        </div>

        <div className="fx-search">
          <Icon name="search" size={12}/>
          <input
            value={q}
            onChange={e => { setQ(e.target.value); setSel(null); }}
            placeholder={`Buscar en ${carpeta || rootLabel}`}
          />
        </div>

        <div className="fx-views">
          <button
            className={`fx-nav tip${vista === 'detalles' ? ' on' : ''}`}
            data-tip="Vista de detalles"
            onClick={() => setVista('detalles')}
          ><Icon name="list" size={14}/></button>
          <button
            className={`fx-nav tip${vista === 'iconos' ? ' on' : ''}`}
            data-tip="Vista de iconos"
            onClick={() => setVista('iconos')}
          ><Icon name="grid" size={14}/></button>
        </div>

        <label className={`btn primary${puedeSubir ? '' : ' disabled'}`} style={{cursor: puedeSubir ? 'pointer' : 'not-allowed'}} title={puedeSubir ? 'Subir archivos a esta carpeta' : 'Entra en una carpeta para subir'}>
          <Icon name="upload" size={12}/> {uploading ? 'Subiendo…' : 'Subir'}
          <input
            ref={inputRef}
            type="file"
            multiple
            accept=".jpg,.jpeg,.png,.webp,.gif,.heic,.pdf,.doc,.docx,.xls,.xlsx,.csv,.ppt,.pptx,.txt,.dwg,.dxf,.dwf,.step,.stp,.iges,.igs,.3dm,.skp,.zip"
            style={{display:'none'}}
            onChange={e => subirArchivos(e.target.files)}
            disabled={!puedeSubir || uploading}
          />
        </label>
      </div>

      <div className="fx-body">
        {/* Panel de navegación */}
        <nav className="fx-tree">
          {parentCrumbs.map((pc, i) => (
            <button key={`tp-${i}`} className="fx-tree-item root" onClick={pc.onClick}>
              <Icon name="arrow_up" size={14}/> <span>{pc.label}</span>
            </button>
          ))}
          <button className={`fx-tree-item root${carpeta ? '' : ' on'}`} onClick={subir}>
            <Icon name="folder" size={14}/> <span>{rootLabel}</span>
          </button>
          {subcarpetas.map(sc => (
            <button key={`ts-${sc.key}`} className="fx-tree-item" onClick={sc.onOpen} title={sc.sub || ''}>
              <Icon name="folder" size={14}/> <span>{sc.label}</span>
              <em>{nSub(sc)}</em>
            </button>
          ))}
          {visibles.map(c => (
            <button
              key={c}
              className={`fx-tree-item${carpeta === c ? ' on' : ''}`}
              onClick={() => abrirCarpeta(c)}
            >
              <Icon name="folder" size={14}/> <span>{c}</span>
              <em>{(porCarpeta[c] || []).length}</em>
            </button>
          ))}
        </nav>

        {/* Contenido */}
        <div
          className={`fx-main${dragging ? ' dragging' : ''}`}
          tabIndex={0}
          onKeyDown={onKeyDown}
          onClick={() => setSel(null)}
          onDragOver={e => { e.preventDefault(); if (puedeSubir) setDragging(true); }}
          onDragLeave={() => setDragging(false)}
          onDrop={onDrop}
        >
          {loading && <p className="muted fx-empty">Cargando…</p>}
          {!loading && error && <p className="fx-empty" style={{color:'var(--err)'}}>{error}</p>}

          {!loading && !error && vista === 'detalles' && (
            <table className="fx-table">
              <thead>
                <tr>
                  <th>Nombre</th>
                  <th style={{width:150}}>{buscando && !carpeta ? 'Carpeta' : 'Tipo'}</th>
                  <th style={{width:90,textAlign:'right'}}>Tamaño</th>
                  <th style={{width:140}}>Fecha</th>
                </tr>
              </thead>
              <tbody>
                {mostrarSub.map(sc => (
                  <tr key={`ds-${sc.key}`} className="fx-row folder" onDoubleClick={sc.onOpen}>
                    <td>
                      <span className="fx-icon folder"><Icon name="folder" size={15}/></span>
                      <span className="fx-name" title={sc.sub || ''}>{sc.label}</span>
                      {sc.sub && <span className="muted" style={{marginLeft:8,fontSize:11.5}}>{sc.sub}</span>}
                    </td>
                    <td>Carpeta de proyecto</td>
                    <td style={{textAlign:'right'}}>—</td>
                    <td>{nSub(sc)} archivo{nSub(sc) === 1 ? '' : 's'}</td>
                  </tr>
                ))}
                {mostrarCarpetas && visibles.map(c => (
                  <tr key={`d-${c}`} className="fx-row folder" onDoubleClick={() => abrirCarpeta(c)}>
                    <td>
                      <span className="fx-icon folder"><Icon name="folder" size={15}/></span>
                      <span className="fx-name">{c}</span>
                    </td>
                    <td>Carpeta de archivos</td>
                    <td style={{textAlign:'right'}}>—</td>
                    <td>{(porCarpeta[c] || []).length} elemento{(porCarpeta[c] || []).length === 1 ? '' : 's'}</td>
                  </tr>
                ))}
                {listado.map(f => (
                  <tr
                    key={f.id}
                    className={`fx-row${sel === f.id ? ' sel' : ''}`}
                    onClick={e => { e.stopPropagation(); setSel(f.id); }}
                    onDoubleClick={() => abrirArchivo(f)}
                    onContextMenu={e => { e.preventDefault(); e.stopPropagation(); setSel(f.id); setMenu({ x: e.clientX, y: e.clientY, file: f }); }}
                  >
                    <td>
                      <span className="fx-icon"><FxThumb file={f} size={15}/></span>
                      <span className="fx-name" title={f.original_name || ''}>{f.original_name || `Archivo ${f.id}`}</span>
                    </td>
                    <td>{buscando && !carpeta ? carpetaDe(f) : fxTipo(f)}</td>
                    <td style={{textAlign:'right'}}>{archivoSize(f.size)}</td>
                    <td>{fxFecha(f.created_at)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}

          {!loading && !error && vista === 'iconos' && (
            <div className="fx-icons">
              {mostrarSub.map(sc => (
                <button key={`is-${sc.key}`} className="fx-tile folder" onDoubleClick={sc.onOpen} title={sc.sub || ''}>
                  <span className="fx-tile-img folder"><Icon name="folder" size={34}/></span>
                  <span className="fx-tile-name">{sc.label}</span>
                  <span className="fx-tile-sub">{nSub(sc)} archivo{nSub(sc) === 1 ? '' : 's'}</span>
                </button>
              ))}
              {mostrarCarpetas && visibles.map(c => (
                <button key={`i-${c}`} className="fx-tile folder" onDoubleClick={() => abrirCarpeta(c)}>
                  <span className="fx-tile-img folder"><Icon name="folder" size={34}/></span>
                  <span className="fx-tile-name">{c}</span>
                  <span className="fx-tile-sub">{(porCarpeta[c] || []).length} elemento{(porCarpeta[c] || []).length === 1 ? '' : 's'}</span>
                </button>
              ))}
              {listado.map(f => (
                <button
                  key={f.id}
                  className={`fx-tile${sel === f.id ? ' sel' : ''}`}
                  onClick={e => { e.stopPropagation(); setSel(f.id); }}
                  onDoubleClick={() => abrirArchivo(f)}
                  onContextMenu={e => { e.preventDefault(); e.stopPropagation(); setSel(f.id); setMenu({ x: e.clientX, y: e.clientY, file: f }); }}
                >
                  <span className="fx-tile-img"><FxThumb file={f} size={30}/></span>
                  <span className="fx-tile-name" title={f.original_name || ''}>{f.original_name || `Archivo ${f.id}`}</span>
                  <span className="fx-tile-sub">{archivoSize(f.size)}</span>
                </button>
              ))}
            </div>
          )}

          {!loading && !error && nElementos === 0 && (
            <p className="muted fx-empty">
              {buscando ? 'Ningún archivo coincide con la búsqueda.' : 'Esta carpeta está vacía. Arrastra archivos aquí o pulsa «Subir».'}
            </p>
          )}

          {dragging && <div className="fx-drop">Suelta para subir a «{carpeta}»</div>}
        </div>
      </div>

      {/* Barra de estado */}
      <div className="fx-status">
        <span>{nElementos} elemento{nElementos === 1 ? '' : 's'}</span>
        {seleccionado && (
          <span>1 elemento seleccionado · {archivoSize(totalSel)}</span>
        )}
        <span className="fx-status-actions">
          <button className="btn ghost" disabled={!seleccionado} onClick={() => seleccionado && abrirArchivo(seleccionado)}>
            <Icon name="eye" size={12}/> Abrir
          </button>
          <button className="btn ghost" disabled={!seleccionado} onClick={() => seleccionado && descargar(seleccionado)}>
            <Icon name="download" size={12}/> Descargar
          </button>
          <button className="btn ghost" style={{color:'var(--err)'}} disabled={!seleccionado} onClick={() => seleccionado && eliminar(seleccionado)}>
            <Icon name="trash" size={12}/> Eliminar
          </button>
        </span>
      </div>

      {/* Menú contextual */}
      {menu && (
        <div className="fx-menu" style={{left: menu.x, top: menu.y}} onClick={e => e.stopPropagation()}>
          <button onClick={() => { setMenu(null); abrirArchivo(menu.file); }}><Icon name="eye" size={12}/> Abrir</button>
          <button onClick={() => { setMenu(null); descargar(menu.file); }}><Icon name="download" size={12}/> Descargar</button>
          <button className="danger" onClick={() => { setMenu(null); eliminar(menu.file); }}><Icon name="trash" size={12}/> Eliminar</button>
        </div>
      )}
    </div>
  );
};

window.ExploradorArchivos = ExploradorArchivos;
