/* ============================================================
   FileManager — femtoAI EVK file management panel
   Exported to window.FileManager for use in app.jsx
   ============================================================ */
const { useState, useEffect, useCallback, useRef } = React;

/* ---- Icon helper (same inline SVG approach as app.jsx) ---- */
const FM_ICONS = {
  refresh:   '<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/>',
  stop:      '<rect width="14" height="14" x="5" y="5" rx="2"/>',
  play:      '<polygon points="6 3 20 12 6 21 6 3"/>',
  reboot:    '<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/><path d="M16 16h5v5"/>',
  trash:     '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>',
  upload:    '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>',
  download:  '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>',
  file:      '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>',
  filejson:  '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 12.5v1a1.5 1.5 0 0 1-3 0v-1"/><path d="M14 11h1.5a1.5 1.5 0 0 1 0 3H14v1.5"/>',
  check:     '<path d="M20 6 9 17l-5-5"/>',
  x:         '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
  alert:     '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
  chip:      '<path d="M12 2H2v10l9.29 9.29c.94.94 2.48.94 3.42 0l6.58-6.58c.94-.94.94-2.48 0-3.42L12 2Z"/><path d="M7 7h.01"/>',
  pin:       '<line x1="12" x2="12" y1="17" y2="22"/><path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z"/>',
};
function FMIcon({ name, style }) {
  return (
    <svg style={{ width: 16, height: 16, flexShrink: 0, ...style }}
      viewBox="0 0 24 24" fill="none" stroke="currentColor"
      strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"
      dangerouslySetInnerHTML={{ __html: FM_ICONS[name] || "" }} />
  );
}

/* ---- Helpers ------------------------------------------------ */
function fmtSize(n) {
  if (n == null) return "—";
  if (n < 1024) return n + " B";
  if (n < 1048576) return (n / 1024).toFixed(1) + " KB";
  return (n / 1048576).toFixed(2) + " MB";
}
function fmtRate(bps) {
  if (bps < 1024) return bps.toFixed(0) + " B/s";
  if (bps < 1048576) return (bps / 1024).toFixed(1) + " KB/s";
  return (bps / 1048576).toFixed(2) + " MB/s";
}
function fileType(name) {
  if (!name) return "bin";
  const ext = name.split(".").pop().toLowerCase();
  if (ext === "json") return "json";
  if (ext === "femto") return "femto";
  return ext || "bin";
}
function fileBadgeStyle(type) {
  if (type === "json")  return { background: "rgba(48,209,255,0.15)", color: "var(--color-flow)",   border: "1px solid rgba(48,209,255,0.35)" };
  if (type === "femto") return { background: "rgba(249,128,61,0.14)", color: "var(--color-spark)",  border: "1px solid rgba(249,128,61,0.4)" };
  return                       { background: "rgba(169,137,247,0.14)", color: "var(--color-purple)", border: "1px solid rgba(169,137,247,0.4)" };
}

/* ---- Sub-components ----------------------------------------- */

function StatusCard({ ping, status, loading, onStop, onStart, onReboot, onRefresh, uploading, onDfu, dfuPending }) {
  const stateColor = {
    RUNNING: "var(--color-pulse)",
    LOOPBACK: "var(--color-flow)",
  }[status?.state] || "var(--fg-subtle)";

  return (
    <div className="fm-status-card">
      <div className="fm-status-fields">
        <div className="fm-field">
          <span className="fm-label">Firmware</span>
          <span className="fm-value mono">{ping || (loading ? "…" : "—")}</span>
        </div>
        <div className="fm-field">
          <span className="fm-label">Device state</span>
          <span className="fm-value" style={{ display: "flex", alignItems: "center", gap: 7 }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: stateColor, flexShrink: 0, boxShadow: `0 0 0 3px ${stateColor}33` }}></span>
            {status?.state || (loading ? "…" : "—")}
          </span>
        </div>
        <div className="fm-field">
          <span className="fm-label">Config</span>
          <span className="fm-value mono" title={status?.config}>{status?.config || "—"}</span>
        </div>
        <div className="fm-field">
          <span className="fm-label">Model</span>
          <span className="fm-value mono" title={status?.model}>{status?.model || "—"}</span>
        </div>
      </div>
      <div className="fm-status-actions">
        <button className="fm-btn fm-btn-ghost" onClick={onRefresh} disabled={loading || uploading} title="Refresh status and file list">
          <FMIcon name="refresh" />{loading ? "Refreshing…" : "Refresh"}
        </button>
        <button className="fm-btn fm-btn-ghost" onClick={onStop} disabled={loading || uploading || status?.state === "STOPPED"} title="CMD:STOP — halt audio engine">
          <FMIcon name="stop" />Stop
        </button>
        <button className="fm-btn fm-btn-ghost" onClick={onStart} disabled={loading || uploading || status?.state === "RUNNING"} title="CMD:START — resume audio engine">
          <FMIcon name="play" />Start
        </button>
        <button className="fm-btn fm-btn-danger" onClick={onReboot} disabled={uploading} title="CMD:REBOOT — applies new files">
          <FMIcon name="reboot" />Reboot
        </button>
        <button className="fm-btn fm-btn-ghost" onClick={onDfu} disabled={loading || uploading || dfuPending} title="CMD:DFU — flash hex file from SD card">
          <FMIcon name="chip" />{dfuPending ? "Sending…" : "Update firmware"}
        </button>
      </div>
    </div>
  );
}

function DropZone({ onFile, disabled }) {
  const [dragging, setDragging] = useState(false);
  const inputRef = useRef(null);

  const handleDrop = (e) => {
    e.preventDefault();
    setDragging(false);
    if (disabled) return;
    const f = e.dataTransfer.files[0];
    if (f) onFile(f);
  };
  const handleChange = (e) => {
    const f = e.target.files[0];
    if (f) { onFile(f); e.target.value = ""; }
  };

  return (
    <div
      className={"fm-dropzone" + (dragging ? " fm-dropzone--over" : "") + (disabled ? " fm-dropzone--disabled" : "")}
      onDragOver={(e) => { e.preventDefault(); if (!disabled) setDragging(true); }}
      onDragLeave={() => setDragging(false)}
      onDrop={handleDrop}
      onClick={() => !disabled && inputRef.current?.click()}
    >
      <input ref={inputRef} type="file" style={{ display: "none" }} onChange={handleChange} />
      <FMIcon name="upload" style={{ width: 30, height: 30, color: "var(--color-indigo)", opacity: disabled ? 0.4 : 1 }} />
      <div className="fm-dropzone-title">{disabled ? "Disconnect upload to queue another" : "Drop a file here, or click to browse"}</div>
      <div className="fm-dropzone-hint">.femto model files · system_config.json · any binary</div>
    </div>
  );
}

function UploadProgress({ file, progress, onCancel }) {
  const pct = progress.totalBytes > 0 ? Math.round((progress.bytesTransferred / progress.totalBytes) * 100) : 0;
  return (
    <div className="fm-upload-progress">
      <div className="fm-up-header">
        <FMIcon name="upload" style={{ color: "var(--color-spark)" }} />
        <span className="fm-up-name">{file?.name}</span>
        <span className="fm-up-stats">{fmtSize(progress.bytesTransferred)} / {fmtSize(progress.totalBytes)} · {fmtRate(progress.rateBps || 0)}</span>
        {onCancel && (
          <button className="fm-up-cancel" onClick={onCancel} title="Cancel upload">
            <FMIcon name="x" />
          </button>
        )}
      </div>
      <div className="fm-progress-track">
        <div className="fm-progress-fill" style={{ width: pct + "%" }}></div>
      </div>
      <div className="fm-up-sub">
        Chunk {progress.chunk} of {progress.totalChunks} · {pct}%
      </div>
    </div>
  );
}

function FileRow({ file, onDelete, onDownload, downloading, uploading }) {
  const [confirming, setConfirming] = useState(false);
  const type = fileType(file.name);
  const badgeStyle = fileBadgeStyle(type);
  const isDownloading = downloading === file.name;

  const handleDelete = () => {
    if (confirming) { onDelete(file.name); setConfirming(false); }
    else setConfirming(true);
  };

  return (
    <div className="fm-file-row">
      <FMIcon name={type === "json" ? "filejson" : "file"} style={{ color: "var(--fg-muted)" }} />
      <span className="fm-file-name" title={file.name}>{file.name}</span>
      <span className="fm-file-badge" style={badgeStyle}>{type}</span>
      <span className="fm-file-size">{fmtSize(file.size)}</span>
      {confirming ? (
        <div style={{ display: "flex", gap: 6 }}>
          <button className="fm-btn fm-btn-danger fm-btn-sm" onClick={handleDelete} title="Confirm delete">
            <FMIcon name="check" />Delete
          </button>
          <button className="fm-btn fm-btn-ghost fm-btn-sm" onClick={() => setConfirming(false)}>
            <FMIcon name="x" />Cancel
          </button>
        </div>
      ) : (
        <div style={{ display: "flex", gap: 6 }}>
          <button className="fm-btn-icon fm-btn-icon--download" onClick={() => onDownload(file.name)}
            disabled={uploading || !!downloading} title={"Download " + file.name}>
            <FMIcon name={isDownloading ? "refresh" : "download"}
              style={isDownloading ? { animation: "spin 1s linear infinite" } : undefined} />
          </button>
          <button className="fm-btn-icon" onClick={handleDelete} disabled={uploading || !!downloading} title={"Delete " + file.name}>
            <FMIcon name="trash" />
          </button>
        </div>
      )}
    </div>
  );
}

function QueuedFile({ file, onRemove }) {
  const type = fileType(file.name);
  const badgeStyle = fileBadgeStyle(type);
  const isConfig = file.name === "system_config.json";
  const isFemto = type === "femto";

  return (
    <div className="fm-queued-file">
      <FMIcon name={type === "json" ? "filejson" : "file"} style={{ color: "var(--color-indigo)" }} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <span className="fm-file-name">{file.name}</span>
          <span className="fm-file-badge" style={badgeStyle}>{type}</span>
          {(isConfig || isFemto) && (
            <span className="fm-file-badge" style={{ background: "rgba(109,229,190,0.15)", color: "#1aa86a", border: "1px solid rgba(109,229,190,0.4)" }}>
              {isConfig ? "auto-applied on reboot" : "programs on reboot"}
            </span>
          )}
        </div>
        <div style={{ fontSize: "var(--text-xs)", color: "var(--fg-subtle)", marginTop: 3 }}>{fmtSize(file.size)}</div>
      </div>
      <button className="fm-btn-icon" onClick={onRemove} title="Remove">
        <FMIcon name="x" />
      </button>
    </div>
  );
}

/* ---- Main FileManager component ----------------------------- */
function FileManager({ protocolCtrl, connState, addLog, dfuPhase, onDfuTriggered, onDfuDismiss }) {
  const [loading, setLoading]           = useState(false);
  const [error, setError]               = useState(null);
  const [dfuPending, setDfuPending]     = useState(false);
  const [dfuError, setDfuError]         = useState(null);
  const [pingVer, setPingVer]           = useState(null);
  const [statusData, setStatusData]     = useState(null);
  const [sdFiles, setSdFiles]           = useState([]);
  const [queuedFile, setQueuedFile]     = useState(null);
  const [uploadState, setUploadState]   = useState("idle"); // idle | uploading | done | error
  const [uploadProgress, setUploadProgress] = useState({ chunk: 0, totalChunks: 0, bytesTransferred: 0, totalBytes: 0, rateBps: 0 });
  const [uploadError, setUploadError]   = useState(null);
  const [downloading, setDownloading]   = useState(null);
  const abortRef = useRef(null);

  const isOpen = connState === "open";
  const uploading = uploadState === "uploading";

  const refresh = useCallback(async () => {
    if (!isOpen) return;
    setLoading(true);
    setError(null);
    try {
      const [ver, st, files] = await Promise.allSettled([
        protocolCtrl.ping(),
        protocolCtrl.status(),
        protocolCtrl.list(),
      ]);
      if (ver.status === "fulfilled")   setPingVer(ver.value);
      if (st.status === "fulfilled")    setStatusData(st.value);
      if (files.status === "fulfilled") setSdFiles(files.value);
      const firstErr = [ver, st, files].find(r => r.status === "rejected");
      if (firstErr) setError(firstErr.reason?.message || "Refresh failed.");
    } finally {
      setLoading(false);
    }
  }, [isOpen, protocolCtrl]);

  // Auto-refresh when tab is opened and device is connected
  useEffect(() => {
    if (isOpen) refresh();
  }, [isOpen]); // eslint-disable-line

  const handleStop = async () => {
    try {
      await protocolCtrl.stop();
      addLog("Device stopped.");
      setStatusData(s => s ? { ...s, state: "STOPPED" } : s);
    } catch (e) { setError(e.message); }
  };
  const handleStart = async () => {
    try {
      await protocolCtrl.start();
      addLog("Device started.");
      setStatusData(s => s ? { ...s, state: "RUNNING" } : s);
    } catch (e) { setError(e.message); }
  };
  const handleReboot = async () => {
    addLog("Rebooting device…");
    await protocolCtrl.reboot();
    addLog("Reboot command sent. Device will reconnect shortly.");
    setSdFiles([]);
    setStatusData(null);
    setPingVer(null);
  };
  const handleDelete = async (filename) => {
    try {
      await protocolCtrl.deleteFile(filename);
      addLog("Deleted: " + filename);
      setSdFiles(prev => prev.filter(f => f.name !== filename));
    } catch (e) { setError(e.message); }
  };

  const handleDownload = useCallback(async (filename) => {
    if (downloading) return;
    setDownloading(filename);
    setError(null);
    try {
      addLog("Downloading: " + filename);
      const bytes = await protocolCtrl.read(filename);
      const blob = new Blob([bytes]);
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
      addLog("Downloaded: " + filename + " (" + fmtSize(bytes.length) + ")");
    } catch (e) {
      setError("Download failed: " + e.message);
      addLog("Download error: " + e.message);
    } finally {
      setDownloading(null);
    }
  }, [downloading, protocolCtrl, addLog]);

  const handleUpload = async () => {
    if (!queuedFile || uploading) return;
    const ac = new AbortController();
    abortRef.current = ac;
    setUploadState("uploading");
    setUploadError(null);
    setUploadProgress({ chunk: 0, totalChunks: Math.ceil(queuedFile.size / 512), bytesTransferred: 0, totalBytes: queuedFile.size, rateBps: 0 });
    try {
      await protocolCtrl.upload(queuedFile, {
        signal: ac.signal,
        onProgress: p => setUploadProgress(p),
        onLog: msg => addLog(msg),
      });
      setUploadState("done");
      setQueuedFile(null);
      await refresh();
    } catch (e) {
      if (e.message === "Upload cancelled.") {
        setUploadState("idle");
        addLog("Upload cancelled.");
      } else {
        setUploadState("error");
        setUploadError(e.message);
        addLog("Upload error: " + e.message);
      }
    }
    abortRef.current = null;
  };

  const handleCancel = () => {
    if (abortRef.current) abortRef.current.abort();
  };

  const handleDfu = async () => {
    setDfuError(null);
    const hexFiles = sdFiles.filter(f => f.name.toLowerCase().endsWith(".hex"));
    if (hexFiles.length === 0) {
      setDfuError("No hex file on SD card — please upload a hex file to update firmware.");
      return;
    }
    if (hexFiles.length > 1) {
      setDfuError("Multiple hex files detected — please ensure there is only one hex file on the SD card before continuing.");
      return;
    }
    setDfuPending(true);
    try {
      addLog("Sending CMD:DFU…");
      await protocolCtrl.dfu();
      addLog("DFU acknowledged — device is flashing. Do not disconnect.");
      onDfuTriggered();
    } catch (e) {
      setDfuError(e.message || "DFU command failed.");
      addLog("DFU error: " + (e.message || "unknown"));
    } finally {
      setDfuPending(false);
    }
  };

  if (!isOpen) {
    if (dfuPhase === "flashing") {
      return (
        <div className="fm-empty">
          <FMIcon name="chip" style={{ width: 44, height: 44, color: "var(--color-indigo)", opacity: 0.6 }} />
          <div className="fm-empty-title">Flashing firmware…</div>
          <div className="fm-empty-sub">The device is writing new firmware. Do not disconnect. This may take 10–30 seconds.</div>
        </div>
      );
    }
    if (dfuPhase === "reconnecting") {
      return (
        <div className="fm-empty">
          <FMIcon name="chip" style={{ width: 44, height: 44, color: "var(--color-indigo)", opacity: 0.6 }} />
          <div className="fm-empty-title">Reconnecting…</div>
          <div className="fm-empty-sub">Waiting for device to come back online.</div>
        </div>
      );
    }
    if (dfuPhase === "timeout") {
      return (
        <div className="fm-empty">
          <FMIcon name="alert" style={{ width: 44, height: 44, color: "var(--color-spark)", opacity: 0.7 }} />
          <div className="fm-empty-title">Reconnection timed out</div>
          <div className="fm-empty-sub">
            The device did not reconnect automatically.{" "}
            <span style={{ cursor: "pointer", textDecoration: "underline" }} onClick={onDfuDismiss}>Connect manually</span>.
          </div>
        </div>
      );
    }
    return (
      <div className="fm-empty">
        <FMIcon name="chip" style={{ width: 44, height: 44, color: "var(--color-indigo)", opacity: 0.4 }} />
        <div className="fm-empty-title">No device connected</div>
        <div className="fm-empty-sub">Connect to an EVK2 from the toolbar to manage SD card files.</div>
      </div>
    );
  }

  return (
    <div className="fm-wrap">
      <StatusCard
        ping={pingVer}
        status={statusData}
        loading={loading}
        uploading={uploading}
        onStop={handleStop}
        onStart={handleStart}
        onReboot={handleReboot}
        onRefresh={refresh}
        onDfu={handleDfu}
        dfuPending={dfuPending}
      />

      {dfuPhase === "done" && (
        <div className="fm-success-banner">
          <FMIcon name="check" style={{ color: "#1aa86a" }} />
          Firmware updated successfully.
          <button className="fm-btn-icon" onClick={onDfuDismiss} style={{ marginLeft: "auto" }}><FMIcon name="x" /></button>
        </div>
      )}

      {dfuError && (
        <div className="fm-error-banner">
          <FMIcon name="alert" style={{ color: "var(--color-spark)" }} />
          {dfuError}
          <button className="fm-btn-icon" onClick={() => setDfuError(null)} style={{ marginLeft: "auto" }}><FMIcon name="x" /></button>
        </div>
      )}

      {error && (
        <div className="fm-error-banner">
          <FMIcon name="alert" style={{ color: "var(--color-spark)" }} />
          {error}
          <button className="fm-btn-icon" onClick={() => setError(null)} style={{ marginLeft: "auto" }}><FMIcon name="x" /></button>
        </div>
      )}

      <div className="fm-grid">
        {/* Upload panel */}
        <div className="fm-panel">
          <div className="fm-panel-title"><FMIcon name="upload" />Upload file</div>

          {uploadState === "done" && (
            <div className="fm-success-banner">
              <FMIcon name="check" style={{ color: "#1aa86a" }} />
              Upload complete.
              <button className="fm-btn-icon" onClick={() => setUploadState("idle")} style={{ marginLeft: "auto" }}><FMIcon name="x" /></button>
            </div>
          )}
          {uploadState === "error" && (
            <div className="fm-error-banner">
              <FMIcon name="alert" style={{ color: "var(--color-spark)" }} />
              {uploadError}
              <button className="fm-btn-icon" onClick={() => setUploadState("idle")} style={{ marginLeft: "auto" }}><FMIcon name="x" /></button>
            </div>
          )}

          {uploading ? (
            <UploadProgress file={queuedFile} progress={uploadProgress} onCancel={handleCancel} />
          ) : (
            <>
              <DropZone onFile={setQueuedFile} disabled={uploading} />
              {queuedFile && (
                <div style={{ marginTop: 12, display: "flex", flexDirection: "column", gap: 10 }}>
                  <QueuedFile file={queuedFile} onRemove={() => { setQueuedFile(null); setUploadState("idle"); }} />
                  <button className="fm-btn fm-btn-primary" onClick={handleUpload} style={{ alignSelf: "flex-end" }}>
                    <FMIcon name="upload" />Upload to SD card
                  </button>
                </div>
              )}
            </>
          )}
        </div>

        {/* File list */}
        <div className="fm-panel">
          <div className="fm-panel-title" style={{ justifyContent: "space-between" }}>
            <span style={{ display: "flex", alignItems: "center", gap: 8 }}><FMIcon name="chip" />SD card — {sdFiles.length} file{sdFiles.length !== 1 ? "s" : ""}</span>
            <button className="fm-btn fm-btn-ghost fm-btn-sm" onClick={refresh} disabled={loading || uploading}><FMIcon name="refresh" /></button>
          </div>
          {loading ? (
            <div className="fm-list-empty">Loading…</div>
          ) : sdFiles.length === 0 ? (
            <div className="fm-list-empty">SD card is empty</div>
          ) : (
            <div className="fm-file-list">
              {sdFiles.map(f => (
                <FileRow key={f.name} file={f} onDelete={handleDelete} onDownload={handleDownload} downloading={downloading} uploading={uploading} />
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { FileManager });
