import { useCallback, useEffect, useMemo, useState } from 'react';
import { api } from '../../api/client';
import './FileManager.css';

interface DirEntry {
  name: string;
  is_dir: boolean;
  size: number;
}

interface Props {
  agentId: string;
  agentName?: string;
  online: boolean;
  /** Called when a command_result arrives (from parent WS hook) */
  commandResults?: { agentId: string; action: string; success: boolean; message: string }[];
}

function parseListDir(message: string): { path: string; entries: DirEntry[] } | null {
  try {
    const j = JSON.parse(message) as { path?: string; entries?: DirEntry[] };
    if (j.entries && Array.isArray(j.entries)) {
      return { path: j.path ?? '', entries: j.entries };
    }
  } catch {
    /* not JSON */
  }
  return null;
}

export default function FileManager({ agentId, agentName, online, commandResults }: Props) {
  const [cwd, setCwd] = useState('C:\\');
  const [entries, setEntries] = useState<DirEntry[]>([]);
  const [filter, setFilter] = useState('');
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [busy, setBusy] = useState(false);
  const [preview, setPreview] = useState('');
  const [err, setErr] = useState('');
  const [uploadPath, setUploadPath] = useState('');

  const filtered = useMemo(() => {
    const q = filter.trim().toLowerCase();
    if (!q) return entries;
    return entries.filter((e) => e.name.toLowerCase().includes(q));
  }, [entries, filter]);

  const refresh = useCallback(() => {
    if (!online || !agentId) return;
    setBusy(true);
    setErr('');
    api.sendAgentCommand(agentId, 'list_dir', { path: cwd }).catch((e) => {
      setErr(e instanceof Error ? e.message : String(e));
      setBusy(false);
    });
  }, [agentId, cwd, online]);

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

  useEffect(() => {
    if (!commandResults?.length) return;
    const last = [...commandResults].reverse().find((r) => r.agentId === agentId);
    if (!last) return;
    if (last.action === 'list_dir' && last.success) {
      const parsed = parseListDir(last.message);
      if (parsed) {
        setEntries(parsed.entries);
        if (parsed.path) setCwd(parsed.path);
      }
      setBusy(false);
    } else if (last.action === 'list_dir' && !last.success) {
      setErr(last.message);
      setBusy(false);
    } else if (last.action === 'read_file' && last.success) {
      setPreview(last.message.slice(0, 8000));
      setBusy(false);
    } else if (last.action === 'read_file' && !last.success) {
      setErr(last.message);
      setBusy(false);
    } else if (last.action === 'download') {
      setBusy(false);
    }
  }, [commandResults, agentId]);

  const navigate = (name: string, isDir: boolean) => {
    if (!isDir) return;
    const sep = cwd.includes('/') ? '/' : '\\';
    let next = cwd.endsWith(sep) ? cwd + name : cwd + sep + name;
    if (name === '..') {
      const parts = cwd.replace(/[/\\]+$/, '').split(/[/\\]/);
      parts.pop();
      next = parts.join(sep) || (sep === '/' ? '/' : 'C:\\');
    }
    setCwd(next);
    setSelected(new Set());
  };

  const toggleSelect = (name: string) => {
    setSelected((prev) => {
      const n = new Set(prev);
      if (n.has(name)) n.delete(name);
      else n.add(name);
      return n;
    });
  };

  const sep = cwd.includes('/') ? '/' : '\\';

  const downloadSelected = async () => {
    if (!online || selected.size === 0) return;
    setBusy(true);
    for (const name of selected) {
      const p = cwd.endsWith(sep) ? cwd + name : cwd + sep + name;
      try {
        const res = await api.sendAgentCommand(agentId, 'download', { path: p });
        if (res && typeof res === 'object' && 'success' in res) {
          /* result via WS */
        }
      } catch (e) {
        setErr(e instanceof Error ? e.message : String(e));
      }
    }
    setBusy(false);
  };

  const readFile = (name: string) => {
    const p = cwd.endsWith(sep) ? cwd + name : cwd + sep + name;
    setBusy(true);
    setPreview('');
    api.sendAgentCommand(agentId, 'read_file', { path: p }).catch((e) => {
      setErr(e instanceof Error ? e.message : String(e));
      setBusy(false);
    });
  };

  const crumbs = cwd.split(/[/\\]/).filter(Boolean);

  return (
    <div className="file-manager">
      <div className="fm-header">
        <span className="font-tech fm-title">FILE BROWSER — {agentName ?? agentId.slice(0, 8)}</span>
        <button type="button" className="btn btn-outline btn-sm" disabled={!online || busy} onClick={refresh}>
          Refresh
        </button>
      </div>
      {!online && <p className="fm-offline">Agent offline</p>}
      <div className="fm-breadcrumb font-tech">
        <button type="button" className="fm-crumb" onClick={() => setCwd(cwd.startsWith('/') ? '/' : 'C:\\')}>root</button>
        {crumbs.map((c, i) => (
          <span key={i}>
            <span className="fm-sep">/</span>
            <button
              type="button"
              className="fm-crumb"
              onClick={() => {
                const parts = crumbs.slice(0, i + 1);
                setCwd((cwd.startsWith('/') ? '/' : '') + parts.join(sep));
              }}
            >
              {c}
            </button>
          </span>
        ))}
      </div>
      <input
        className="input fm-filter"
        placeholder="Filter names…"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
      />
      <ul className="fm-list">
        <li>
          <button type="button" className="fm-row" onClick={() => navigate('..', true)}>..</button>
        </li>
        {filtered.map((e) => (
          <li key={e.name} className={selected.has(e.name) ? 'selected' : ''}>
            <label className="fm-row">
              <input type="checkbox" checked={selected.has(e.name)} onChange={() => toggleSelect(e.name)} />
              <button type="button" className="fm-name" onClick={() => (e.is_dir ? navigate(e.name, true) : readFile(e.name))}>
                {e.is_dir ? '📁' : '📄'} {e.name}
              </button>
              {!e.is_dir && <span className="fm-size">{e.size < 1024 ? `${e.size} B` : `${(e.size / 1024).toFixed(1)} KB`}</span>}
            </label>
          </li>
        ))}
      </ul>
      <div className="fm-actions">
        <button type="button" className="btn btn-outline btn-sm" disabled={!online || selected.size === 0 || busy} onClick={downloadSelected}>
          Download selected
        </button>
        <input className="input mono fm-upload-path" placeholder="Upload path" value={uploadPath} onChange={(e) => setUploadPath(e.target.value)} />
        <label className="btn btn-outline btn-sm">
          Upload
          <input
            type="file"
            hidden
            disabled={!online || busy}
            onChange={async (ev) => {
              const file = ev.target.files?.[0];
              if (!file) return;
              const { readFileAsBase64 } = await import('../../help/desktopPush');
              const b64 = await readFileAsBase64(file);
              const dest = uploadPath.trim() || `${cwd}${sep}${file.name}`;
              setBusy(true);
              api.sendAgentCommand(agentId, 'upload', { path: dest, data: b64 }).finally(() => setBusy(false));
              ev.target.value = '';
            }}
          />
        </label>
      </div>
      {err && <p className="fm-err">{err}</p>}
      {preview && <pre className="fm-preview">{preview}</pre>}
    </div>
  );
}
