import { useEffect, useState, useRef, DragEvent, useCallback } from "react";
import { api, FileEntry, FileCapabilities, FilePreview, SearchHit, formatBytes } from "../api";
import Modal from "../components/Modal";

function FileIcon({ name }: { name: string }) {
  const ext = name.split(".").pop()?.toLowerCase() ?? "";
  if (name === "..") return <span className="text-slate-500">↑</span>;
  if (ext === "png" || ext === "jpg" || ext === "jpeg" || ext === "gif" || ext === "webp" || ext === "svg")
    return <span className="text-blue-400">🖼</span>;
  if (ext === "mp4" || ext === "avi" || ext === "mkv" || ext === "mov")
    return <span className="text-purple-400">🎬</span>;
  if (ext === "mp3" || ext === "wav" || ext === "ogg" || ext === "flac")
    return <span className="text-green-400">🎵</span>;
  if (ext === "zip" || ext === "tar" || ext === "gz" || ext === "rar" || ext === "7z")
    return <span className="text-yellow-400">📦</span>;
  if (ext === "pdf")
    return <span className="text-red-400">📄</span>;
  if (ext === "txt" || ext === "md" || ext === "log" || ext === "cfg" || ext === "conf" || ext === "json" || ext === "yaml" || ext === "yml" || ext === "toml" || ext === "xml")
    return <span className="text-emerald-400">📝</span>;
  return <span className="text-slate-400">📄</span>;
}

function modeStr(mode: number): string {
  const perm = (mode & 0o777).toString(8).padStart(3, "0");
  const type = mode & 0o170000;
  if (type === 0o40000) return `drwxr-xr-x`.slice(0, 10 - perm.length) + perm;
  if (type === 0o120000) return `lrwxr-xr-x`.slice(0, 10 - perm.length) + perm;
  return `-rwxr-xr-x`.slice(0, 10 - perm.length) + perm;
}

export default function Files() {
  const [roots, setRoots] = useState<string[]>([]);
  const [unrestricted, setUnrestricted] = useState(false);
  const [currentPath, setCurrentPath] = useState("/");
  const [entries, setEntries] = useState<FileEntry[]>([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(false);
  const [total, setTotal] = useState(0);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [capabilities, setCapabilities] = useState<FileCapabilities | null>(null);

  const [searchQuery, setSearchQuery] = useState("");
  const [searchResults, setSearchResults] = useState<SearchHit[]>([]);
  const [searching, setSearching] = useState(false);

  const [pathInput, setPathInput] = useState(currentPath);
  const [editingPath, setEditingPath] = useState(false);

  const [showMkdir, setShowMkdir] = useState(false);
  const [mkdirName, setMkdirName] = useState("");

  const [selectedEntry, setSelectedEntry] = useState<FileEntry | null>(null);
  const [preview, setPreview] = useState<FilePreview | null>(null);
  const [previewLoading, setPreviewLoading] = useState(false);
  const [showPreview, setShowPreview] = useState(false);

  const [renameState, setRenameState] = useState<{ entry: FileEntry; name: string } | null>(null);
  const [chmodState, setChmodState] = useState<{ entry: FileEntry; mode: string } | null>(null);
  const [chownState, setChownState] = useState<{ entry: FileEntry; uid: string; gid: string } | null>(null);

  const [isDragOver, setIsDragOver] = useState(false);
  const [uploading, setUploading] = useState(false);

  const fileInputRef = useRef<HTMLInputElement>(null);
  const LIMIT = 200;

  useEffect(() => {
    api.fileCapabilities().then(setCapabilities).catch(() => {});
    api.fileRoots().then(r => {
      setRoots(r.roots ?? []);
      setUnrestricted(r.unrestricted);
      if ((r.roots ?? []).length > 0) {
        setCurrentPath(r.roots![0]);
      } else if (!r.unrestricted) {
        setCurrentPath("/");
      }
    }).catch(() => {});
  }, []);

  useEffect(() => {
    if (currentPath) {
      loadDir(currentPath, 1);
    }
  }, [currentPath]);

  useEffect(() => {
    setSearchResults([]);
    setSearchQuery("");
  }, [currentPath]);

  useEffect(() => {
    if (!editingPath) setPathInput(currentPath);
  }, [currentPath, editingPath]);

  async function loadDir(path: string, pageNum: number) {
    setLoading(true);
    setError(null);
    try {
      const res = await api.listFiles(path, pageNum, LIMIT);
      const entries = res.entries ?? [];
      if (pageNum === 1) {
        setEntries(entries);
      } else {
        setEntries(prev => [...prev, ...entries]);
      }
      setPage(pageNum);
      setHasMore(res.has_more);
      setTotal(res.total);
    } catch (e) {
      setError(e instanceof Error ? e.message : "Error loading directory");
    } finally {
      setLoading(false);
    }
  }

  function refresh() {
    loadDir(currentPath, 1);
  }

  function navigateTo(path: string) {
    setSelectedEntry(null);
    setPreview(null);
    setShowPreview(false);
    setCurrentPath(path);
  }

  async function handleNavigateToPath(e: React.FormEvent) {
    e.preventDefault();
    const path = pathInput.trim() || "/";
    try {
      const info = await api.fileInfo(path);
      if (!info.is_dir) {
        setError("La ruta no es un directorio");
        return;
      }
      navigateTo(path);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Ruta no accesible");
    }
  }

  function handleDoubleClick(entry: FileEntry) {
    if (entry.is_dir) {
      navigateTo(entry.path);
    } else {
      openPreview(entry);
    }
  }

  async function openPreview(entry: FileEntry) {
    setSelectedEntry(entry);
    setPreviewLoading(true);
    setShowPreview(true);
    try {
      const p = await api.filePreview(entry.path);
      setPreview(p);
    } catch (e) {
      setPreview({ type: "binary", mime: "", size: entry.size });
    } finally {
      setPreviewLoading(false);
    }
  }

  function parentDir(): string | null {
    if (currentPath === "/" || currentPath === "") return null;
    const parts = currentPath.split("/").filter(Boolean);
    parts.pop();
    const parent = "/" + parts.join("/");
    return parent === "/" ? "/" : parent;
  }

  async function handleMkdir() {
    if (!mkdirName.trim()) return;
    try {
      await api.mkdirFile(currentPath + "/" + mkdirName.trim());
      setMkdirName("");
      setShowMkdir(false);
      refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Error creating folder");
    }
  }

  async function handleRename() {
    if (!renameState) return;
    try {
      await api.renameFile(renameState.entry.path, renameState.name);
      setRenameState(null);
      refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Error renaming");
    }
  }

  async function handleChmod() {
    if (!chmodState) return;
    try {
      await api.chmodFile(chmodState.entry.path, chmodState.mode);
      setChmodState(null);
      refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Error changing permissions");
    }
  }

  async function handleChown() {
    if (!chownState) return;
    const uid = parseInt(chownState.uid, 10);
    const gid = parseInt(chownState.gid, 10);
    if (isNaN(uid) || isNaN(gid)) {
      setError("UID y GID deben ser numéricos");
      return;
    }
    try {
      await api.chownFile(chownState.entry.path, uid, gid);
      setChownState(null);
      refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Error changing owner");
    }
  }

  async function handleDelete(entry: FileEntry) {
    if (!confirm(`¿Eliminar "${entry.name}"${entry.is_dir ? " y todo su contenido" : ""}?`)) return;
    try {
      await api.deleteFile(entry.path);
      if (selectedEntry?.path === entry.path) {
        setSelectedEntry(null);
        setPreview(null);
        setShowPreview(false);
      }
      refresh();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Error deleting");
    }
  }

  async function handleUpload(fileList: FileList | null) {
    if (!fileList || fileList.length === 0) return;
    setUploading(true);
    for (const file of Array.from(fileList)) {
      try {
        await api.uploadFile(currentPath, file);
      } catch (e) {
        setError(e instanceof Error ? e.message : `Error uploading ${file.name}`);
      }
    }
    setUploading(false);
    refresh();
  }

  async function doSearch(q: string) {
    if (!q.trim()) {
      setSearchResults([]);
      return;
    }
    setSearching(true);
    try {
      const res = await api.searchFiles(currentPath, q, 100);
      setSearchResults(res.results ?? []);
    } catch (e) {
      setError(e instanceof Error ? e.message : "Error searching");
    } finally {
      setSearching(false);
    }
  }

  const handleSearchSubmit = useCallback((e: React.FormEvent) => {
    e.preventDefault();
    doSearch(searchQuery);
  }, [searchQuery, currentPath]);

  function handleDropZone(e: DragEvent<HTMLDivElement>) {
    e.preventDefault();
    setIsDragOver(false);
    handleUpload(e.dataTransfer.files);
  }

  function handleDragOver(e: DragEvent<HTMLDivElement>) {
    e.preventDefault();
    setIsDragOver(true);
  }

  function handleDragLeave() {
    setIsDragOver(false);
  }

  function selectSearchResult(hit: SearchHit) {
    const dir = hit.path.substring(0, hit.path.lastIndexOf("/")) || "/";
    navigateTo(dir);
    setSelectedEntry({ name: hit.name, path: hit.path, is_dir: hit.is_dir, size: hit.size, mode: "", mode_num: 0, mod_time: 0 });
  }

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <h1 className="text-2xl font-bold text-white">Archivos</h1>
        <div className="flex gap-2 items-center">
          <button className="btn-ghost" onClick={refresh}>↻ Actualizar</button>
        </div>
      </div>

      {error && (
        <div className="rounded-md bg-red-500/15 px-3 py-2 text-sm text-red-200">{error}</div>
      )}

      <div className="flex items-center gap-3 flex-wrap">
        {(roots ?? []).length > 1 && (
          <select
            className="input w-auto"
            value={roots.includes(currentPath) ? currentPath : roots[0]}
            onChange={e => navigateTo(e.target.value)}
          >
            {roots.map(r => (
              <option key={r} value={r}>{r}</option>
            ))}
          </select>
        )}
        {unrestricted && (
          <span className="text-xs text-amber-400 bg-amber-500/10 px-2 py-1 rounded border border-amber-700/50">
            Sin restricción — acceso a todo el FS
          </span>
        )}
        <div className="flex items-center gap-2 text-sm text-slate-300 flex-1 min-w-0">
          <button
            className="btn-ghost px-2 py-1 text-xs"
            onClick={() => parentDir() && navigateTo(parentDir()!)}
            disabled={!parentDir()}
          >←</button>
          <form onSubmit={handleNavigateToPath} className="flex-1 min-w-0">
            <input
              className="input font-mono text-sm w-full"
              value={pathInput}
              placeholder="/ruta/absoluta"
              onFocus={() => setEditingPath(true)}
              onBlur={() => { setEditingPath(false); setPathInput(currentPath); }}
              onChange={e => setPathInput(e.target.value)}
              onKeyDown={e => {
                if (e.key === "Escape") {
                  setPathInput(currentPath);
                  (e.target as HTMLInputElement).blur();
                }
              }}
            />
          </form>
        </div>
      </div>

      <form onSubmit={handleSearchSubmit} className="flex gap-2">
        <input
          className="input flex-1"
          placeholder="Buscar archivos y carpetas…"
          value={searchQuery}
          onChange={e => setSearchQuery(e.target.value)}
        />
        <button type="submit" className="btn-primary" disabled={searching}>
          {searching ? "Buscando…" : "🔍"}
        </button>
      </form>

        {(searchResults ?? []).length > 0 && (
          <div className="card">
            <div className="text-sm text-slate-400 mb-2">Resultados ({(searchResults ?? []).length})</div>
          <div className="max-h-48 overflow-y-auto">
            {searchResults.map((hit, i) => (
              <div
                key={i}
                className="flex items-center gap-2 py-1 px-2 hover:bg-slate-800 cursor-pointer rounded"
                onClick={() => selectSearchResult(hit)}
                onDoubleClick={() => {
                  if (hit.is_dir) {
                    navigateTo(hit.path);
                  } else {
                    setSelectedEntry({ name: hit.name, path: hit.path, is_dir: hit.is_dir, size: hit.size, mode: "", mode_num: 0, mod_time: 0 });
                    openPreview({ name: hit.name, path: hit.path, is_dir: hit.is_dir, size: hit.size, mode: "", mode_num: 0, mod_time: 0 });
                  }
                }}
              >
                <FileIcon name={hit.name} />
                <span className="text-slate-100 text-sm truncate">{hit.path}</span>
                <span className="text-slate-500 text-xs ml-auto">{hit.is_dir ? "carpeta" : formatBytes(hit.size)}</span>
              </div>
            ))}
          </div>
          <button className="btn-ghost text-xs mt-2 w-full" onClick={() => { setSearchResults([]); setSearchQuery(""); }}>
            Limpiar búsqueda
          </button>
        </div>
      )}

      <div className="flex gap-2">
        <button className="btn-ghost text-xs" onClick={() => { setShowMkdir(true); setMkdirName(""); }}>📁 Nueva carpeta</button>
        <input ref={fileInputRef} type="file" multiple className="hidden" onChange={e => handleUpload(e.target.files)} />
        <button className="btn-ghost text-xs" onClick={() => fileInputRef.current?.click()} disabled={uploading}>
          {uploading ? "↑ Subiendo…" : "⬆ Subir archivo"}
        </button>
        {capabilities?.chown && (
          <button
            className="btn-ghost text-xs ml-auto"
            onClick={() => {
              if (!selectedEntry) return;
              setChownState({ entry: selectedEntry, uid: String(selectedEntry.mode_num >> 16), gid: String(selectedEntry.mode_num & 0xFFFF) });
            }}
            disabled={!selectedEntry}
          >
            👤 Propietario
          </button>
        )}
        {capabilities?.chmod && (
          <button
            className="btn-ghost text-xs"
            onClick={() => {
              if (!selectedEntry) return;
              setChmodState({ entry: selectedEntry, mode: String((selectedEntry.mode_num & 0o777).toString(8)) });
            }}
            disabled={!selectedEntry}
          >
            🔒 Permisos
          </button>
        )}
      </div>

      <div
        className={`card overflow-x-auto p-0 relative ${isDragOver ? "ring-2 ring-brand-500" : ""}`}
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        onDrop={handleDropZone}
      >
        {isDragOver && (
          <div className="absolute inset-0 bg-brand-500/20 flex items-center justify-center z-10 rounded-lg">
            <span className="text-brand-300 font-medium">Suelta para subir</span>
          </div>
        )}
        <table className="w-full text-left text-sm">
          <thead className="border-b border-slate-800 text-slate-400">
            <tr>
              <th className="px-4 py-3">Nombre</th>
              <th className="px-4 py-3">Tamaño</th>
              <th className="px-4 py-3">Permisos</th>
              <th className="px-4 py-3">Modificado</th>
              <th className="px-4 py-3"></th>
            </tr>
          </thead>
          <tbody>
            {parentDir() !== null && (
              <tr
                className="cursor-pointer hover:bg-slate-800"
                onDoubleClick={() => navigateTo(parentDir()!)}
              >
                <td className="px-4 py-2 text-slate-400">..</td>
                <td className="px-4 py-2 text-slate-500">—</td>
                <td className="px-4 py-2 text-slate-500">—</td>
                <td className="px-4 py-2 text-slate-500">—</td>
                <td className="px-4 py-2"></td>
              </tr>
            )}
            {entries.map(entry => (
              <tr
                key={entry.path}
                className={`cursor-pointer hover:bg-slate-800 ${selectedEntry?.path === entry.path ? "bg-slate-700" : ""}`}
                onClick={() => setSelectedEntry(entry)}
                onDoubleClick={() => handleDoubleClick(entry)}
              >
                <td className="px-4 py-2">
                  <div className="flex items-center gap-2">
                    <FileIcon name={entry.name} />
                    <span className="text-slate-100">{entry.name}</span>
                  </div>
                </td>
                <td className="px-4 py-2 text-slate-400 font-mono text-xs">
                  {entry.is_dir ? "—" : formatBytes(entry.size)}
                </td>
                <td className="px-4 py-2 text-slate-400 font-mono text-xs">
                  {modeStr(entry.mode_num)}
                </td>
                <td className="px-4 py-2 text-slate-400 text-xs">
                  {entry.mod_time > 0 ? new Date(entry.mod_time * 1000).toLocaleString() : "—"}
                </td>
                <td className="px-4 py-2 text-right">
                  <div className="flex gap-1 justify-end">
                    <button
                      className="btn-ghost text-xs px-1 py-0.5"
                      onClick={e => { e.stopPropagation(); setRenameState({ entry, name: entry.name }); }}
                      title="Renombrar"
                    >✎</button>
                    {capabilities?.chmod && (
                      <button
                        className="btn-ghost text-xs px-1 py-0.5"
                        onClick={e => { e.stopPropagation(); setChmodState({ entry, mode: String((entry.mode_num & 0o777).toString(8)) }); }}
                        title="Permisos"
                      >🔒</button>
                    )}
                    {capabilities?.chown && (
                      <button
                        className="btn-ghost text-xs px-1 py-0.5"
                        onClick={e => { e.stopPropagation(); setChownState({ entry, uid: String(entry.mode_num >> 16), gid: String(entry.mode_num & 0xFFFF) }); }}
                        title="Propietario"
                      >👤</button>
                    )}
                    <button
                      className="btn-ghost text-xs px-1 py-0.5 text-red-400"
                      onClick={e => { e.stopPropagation(); handleDelete(entry); }}
                      title="Eliminar"
                    >✕</button>
                  </div>
                </td>
              </tr>
            ))}
            {(entries ?? []).length === 0 && !loading && (
              <tr>
                <td colSpan={5} className="px-4 py-8 text-center text-slate-500">
                  {searchQuery ? "Sin resultados" : "Directorio vacío"}
                </td>
              </tr>
            )}
            {loading && (
              <tr>
                <td colSpan={5} className="px-4 py-6 text-center text-slate-500">
                  Cargando…
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </div>

      {hasMore && (
        <button className="btn-ghost w-full text-xs" onClick={() => loadDir(currentPath, page + 1)}>
          Cargar más ({total - (entries ?? []).length} restantes)
        </button>
      )}

      {showMkdir && (
        <Modal title="Nueva carpeta" onClose={() => setShowMkdir(false)} maxWidth="sm">
          <div className="flex gap-2">
            <input
              className="input flex-1"
              placeholder="Nombre de carpeta"
              value={mkdirName}
              onChange={e => setMkdirName(e.target.value)}
              onKeyDown={e => e.key === "Enter" && handleMkdir()}
              autoFocus
            />
            <button className="btn-primary" onClick={handleMkdir}>Crear</button>
          </div>
        </Modal>
      )}

      {renameState && (
        <Modal title="Renombrar" onClose={() => setRenameState(null)} maxWidth="sm">
          <div className="flex gap-2">
            <input
              className="input flex-1"
              value={renameState.name}
              onChange={e => setRenameState({ ...renameState, name: e.target.value })}
              onKeyDown={e => e.key === "Enter" && handleRename()}
              autoFocus
            />
            <button className="btn-primary" onClick={handleRename}>Renombrar</button>
          </div>
        </Modal>
      )}

      {chmodState && (
        <Modal title="Permisos" onClose={() => setChmodState(null)} maxWidth="sm">
          <div className="space-y-3">
            <div className="flex gap-2 items-center">
              <input
                className="input w-24"
                value={chmodState.mode}
                onChange={e => setChmodState({ ...chmodState, mode: e.target.value })}
                onKeyDown={e => e.key === "Enter" && handleChmod()}
                autoFocus
                placeholder="755"
              />
              <span className="text-slate-400 text-sm">octal (ej: 755, 644)</span>
            </div>
            <div className="text-xs text-slate-500 font-mono">
              {(parseInt(chmodState.mode, 8) || 0).toString(8).padStart(3, "0")} = {[
                ["r", (parseInt(chmodState.mode, 8) || 0) & 0o400],
                ["w", (parseInt(chmodState.mode, 8) || 0) & 0o200],
                ["x", (parseInt(chmodState.mode, 8) || 0) & 0o100],
              ].map(([c, v]) => c + (v ? "✓" : "✗")).join(" ")}
            </div>
            <div className="flex gap-2 justify-end">
              <button className="btn-ghost" onClick={() => setChmodState(null)}>Cancelar</button>
              <button className="btn-primary" onClick={handleChmod}>Aplicar</button>
            </div>
          </div>
        </Modal>
      )}

      {chownState && (
        <Modal title="Cambiar propietario" onClose={() => setChownState(null)} maxWidth="sm">
          <div className="space-y-3">
            <div>
              <label className="label">UID</label>
              <input
                className="input"
                value={chownState.uid}
                onChange={e => setChownState({ ...chownState, uid: e.target.value })}
                placeholder="0"
              />
            </div>
            <div>
              <label className="label">GID</label>
              <input
                className="input"
                value={chownState.gid}
                onChange={e => setChownState({ ...chownState, gid: e.target.value })}
                placeholder="0"
              />
            </div>
            <div className="flex gap-2 justify-end">
              <button className="btn-ghost" onClick={() => setChownState(null)}>Cancelar</button>
              <button className="btn-primary" onClick={handleChown}>Aplicar</button>
            </div>
          </div>
        </Modal>
      )}

      {showPreview && selectedEntry && (
        <Modal
          title={selectedEntry.name}
          onClose={() => { setShowPreview(false); setPreview(null); }}
          maxWidth="3xl"
        >
          <div className="space-y-3">
            {previewLoading ? (
              <div className="text-center text-slate-400 py-8">Cargando…</div>
            ) : preview?.type === "text" ? (
              <div>
                <div className="text-xs text-slate-500 mb-2">{preview.mime} · {formatBytes(preview.size)}</div>
                <pre className="bg-slate-950 rounded p-3 text-xs text-slate-300 overflow-auto max-h-96 font-mono whitespace-pre-wrap break-all">
                  {preview.content}
                </pre>
              </div>
            ) : preview?.type === "image" ? (
              <div>
                <div className="text-xs text-slate-500 mb-2">{preview.mime} · {formatBytes(preview.size)}</div>
                <img
                  src={api.fileDownloadUrl(selectedEntry.path)}
                  alt={selectedEntry.name}
                  className="max-h-96 mx-auto rounded border border-slate-700"
                />
              </div>
            ) : (
              <div className="text-center py-8">
                <div className="text-slate-400 mb-2">Archivo binario</div>
                <div className="text-xs text-slate-500 mb-4">{preview?.mime} · {formatBytes(preview?.size ?? 0)}</div>
                <a
                  href={api.fileDownloadUrl(selectedEntry.path)}
                  download={selectedEntry.name}
                  className="btn-primary"
                >
                  ⬇ Descargar
                </a>
              </div>
            )}
            {!previewLoading && (
              <div className="flex gap-2 justify-end">
                <a
                  href={api.fileDownloadUrl(selectedEntry.path)}
                  download={selectedEntry.name}
                  className="btn-ghost text-xs"
                >
                  ⬇ Descargar
                </a>
                <button className="btn-primary text-xs" onClick={() => { setShowPreview(false); setPreview(null); }}>
                  Cerrar
                </button>
              </div>
            )}
          </div>
        </Modal>
      )}
    </div>
  );
}
