// 文件面板:右栏列表浏览/导航/删除/重命名、刷新、"选入"弹框(跨目录勾选复制/移动)、
// 拖拽上传 overlay + 上传(XHR 带进度)+ 上传状态条。
// 导出 loadFiles / scheduleFilesRefresh(SSE 文件事件触发刷新)/ closeSrcPicker(main Esc 关栈)
// / uploadFiles(聊天区粘贴或拖拽文件复用)。其余入口在本模块顶层自绑。
// 反向依赖:openFilePreview(preview)、logout(auth)、以及 main 的 glue
// downloadFile / selectTask / loadTaskList / loadFolderSuggestions(后续随 media/tasks/newtask 模块化再迁)。
import { state } from "./state.js";
import { $, showMenu } from "./dom.js";
import { api } from "./api.js";
import { escapeHtml, humanSize } from "./format.js";
import { openFilePreview } from "./preview.js";
import { logout } from "./auth.js";
import { downloadFile } from "./media.js";
import { selectTask, loadTaskList } from "./chat.js";
import { loadFolderSuggestions } from "./newtask.js";
import { dialogConfirm, dialogPrompt, message } from "./dialog.js";

// ───── files(user-rooted,不绑 task) ─────
$("btn-refresh-files").onclick = () => loadFiles();
$("btn-upload").onclick = () => $("upload-input").click();
$("upload-input").addEventListener("change", uploadSelected);

// ───── 选入 modal(勾源 → 复制 / 移动到主区当前目录)─────
// 设计:目的地永远是主区 state.filesPath。弹框内浏览的 path 跟主区独立 — 用户从 A 翻到 B
// 勾几个,再翻到 C 接着勾,跨目录 selection 用 Set<rel> 全程保留;切换浏览路径不清空。
const srcPicker = { path: "", selected: new Set() };

async function openSrcPicker() {
  srcPicker.path = "";
  srcPicker.selected.clear();
  const destLabel = state.filesPath ? "我的 / " + state.filesPath : "我的 (根目录)";
  $("sp-dest").textContent = destLabel;
  $("sp-dest").title = destLabel;
  syncSrcCount();
  $("src-picker-modal").classList.add("show");
  await loadSrcPicker();
}

export function closeSrcPicker() {
  $("src-picker-modal").classList.remove("show");
  srcPicker.path = "";
  srcPicker.selected.clear();
}

async function loadSrcPicker() {
  try {
    const qs = srcPicker.path ? "?path=" + encodeURIComponent(srcPicker.path) : "";
    const data = await api("GET", "/v1/files" + qs);
    renderSrcPicker(data);
  } catch (e) {
    if (e.status === 401) { logout(); return; }
    $("sp-list").innerHTML = `<div class="empty">${escapeHtml(e.message)}</div>`;
  }
}

function renderSrcPicker(data) {
  const cr = data.crumbs.map((c, i) => {
    const label = i === 0 ? "我的" : c.label;
    const isLast = i === data.crumbs.length - 1;
    if (isLast) return `<span>${escapeHtml(label)}</span>`;
    return `<a href="#" data-rel="${escapeHtml(c.rel)}">${escapeHtml(label)}</a> /`;
  }).join(" ");
  $("sp-crumbs").innerHTML = cr || `<span class="muted">/</span>`;
  $("sp-crumbs").querySelectorAll("a").forEach((a) => {
    a.onclick = (e) => { e.preventDefault(); srcPicker.path = a.dataset.rel; loadSrcPicker(); };
  });
  const entries = data.entries || [];
  if (!data.exists) {
    $("sp-list").innerHTML = `<div class="empty">(目录尚未创建)</div>`;
    return;
  }
  if (!entries.length) {
    $("sp-list").innerHTML = `<div class="empty">(空目录)</div>`;
    return;
  }
  // 闸:当前浏览路径 == 主区目的地 → 同目录内勾选无意义(同名 409),全行 disabled
  const destPath = state.filesPath || "";
  const sameAsDest = srcPicker.path === destPath;
  $("sp-list").innerHTML = entries.map((e) => {
    const cls = e.is_dir ? "ico-dir" : "ico-file";
    const checked = srcPicker.selected.has(e.rel) ? " checked" : "";
    const disabled = sameAsDest ? " disabled" : "";
    const fullTitle = e.rel || e.name;
    return `
      <div class="sp-row${disabled}" data-rel="${escapeHtml(e.rel)}" data-isdir="${e.is_dir}" title="${escapeHtml(fullTitle)}">
        <input type="checkbox" class="sp-cb" data-rel="${escapeHtml(e.rel)}"${checked}${sameAsDest ? " disabled" : ""} />
        <span class="${cls} sp-name" data-rel="${escapeHtml(e.rel)}" data-isdir="${e.is_dir}" title="${escapeHtml(fullTitle)}">${escapeHtml(e.name)}</span>
        <span class="sp-size">${humanSize(e.size)}</span>
      </div>
    `;
  }).join("");
  $("sp-list").querySelectorAll(".sp-name").forEach((el) => {
    el.onclick = () => {
      if (el.dataset.isdir === "true") {
        srcPicker.path = el.dataset.rel;
        loadSrcPicker();
      }
    };
  });
  $("sp-list").querySelectorAll(".sp-cb").forEach((cb) => {
    cb.onchange = () => {
      const rel = cb.dataset.rel;
      if (cb.checked) srcPicker.selected.add(rel);
      else srcPicker.selected.delete(rel);
      syncSrcCount();
    };
  });
}

function syncSrcCount() {
  const n = srcPicker.selected.size;
  $("sp-count").textContent = String(n);
  $("sp-copy").disabled = n === 0;
  $("sp-move").disabled = n === 0;
}

async function doSrcTransfer(mode) {
  const sources = [...srcPicker.selected];
  if (!sources.length) return;
  const endpoint = mode === "copy" ? "/v1/files/copy" : "/v1/files/move";
  const verb = mode === "copy" ? "复制" : "移动";
  try {
    await api("POST", endpoint, {
      paths: sources,
      dest_dir: state.filesPath || "",
    });
    closeSrcPicker();
    await loadFiles();
    await loadFolderSuggestions();
  } catch (e) {
    if (e.status === 401) { logout(); return; }
    message(verb + "失败:" + e.message, "error");
  }
}

$("btn-src-pick").onclick = openSrcPicker;
$("sp-cancel").onclick = closeSrcPicker;
$("sp-copy").onclick = () => doSrcTransfer("copy");
$("sp-move").onclick = () => doSrcTransfer("move");
$("src-picker-modal").addEventListener("click", (e) => {
  if (e.target.id === "src-picker-modal") closeSrcPicker();
});

// ───── 拖拽上传到主区(目的地 = state.filesPath)─────
// 用 enter/leave 计数避免子元素冒泡时 overlay 闪烁。
let _dragDepth = 0;
function _hasFiles(ev) {
  const t = ev.dataTransfer;
  if (!t) return false;
  if (t.types && [...t.types].includes("Files")) return true;
  return false;
}
$("pane-right").addEventListener("dragenter", (e) => {
  if (!_hasFiles(e)) return;
  e.preventDefault();
  _dragDepth++;
  $("file-droparea").classList.add("show");
});
$("pane-right").addEventListener("dragover", (e) => {
  if (!_hasFiles(e)) return;
  e.preventDefault();
  e.dataTransfer.dropEffect = "copy";
});
$("pane-right").addEventListener("dragleave", (e) => {
  if (!_hasFiles(e)) return;
  _dragDepth = Math.max(0, _dragDepth - 1);
  if (_dragDepth === 0) $("file-droparea").classList.remove("show");
});
$("pane-right").addEventListener("drop", async (e) => {
  if (!_hasFiles(e)) return;
  e.preventDefault();
  _dragDepth = 0;
  $("file-droparea").classList.remove("show");
  const files = Array.from(e.dataTransfer.files || []);
  if (!files.length) return;
  // 落点轻回弹脉冲:一次性,动画结束自摘(避免再次拖入不触发)
  const pane = $("pane-right");
  pane.classList.remove("drop-pulse");
  void pane.offsetWidth; // 强制 reflow 让动画可重放
  pane.classList.add("drop-pulse");
  pane.addEventListener("animationend", () => pane.classList.remove("drop-pulse"), { once: true });
  await uploadFilesWithPaneStatus(files);
});

// 工具调用返回时,右侧文件可能有新增/修改 — debounce 500ms 刷新,避免每次 tool_result 都 hit API
let _filesRefreshTimer = null;
export function scheduleFilesRefresh() {
  clearTimeout(_filesRefreshTimer);
  _filesRefreshTimer = setTimeout(() => { loadFiles(); }, 500);
}

export async function loadFiles() {
  try {
    const qs = state.filesPath ? "?path=" + encodeURIComponent(state.filesPath) : "";
    const data = await api("GET", "/v1/files" + qs);
    renderFiles(data);
  } catch (e) {
    if (e.status === 401) { logout(); return; }
    $("file-crumbs").innerHTML = `<span class="muted">${escapeHtml(e.message)}</span>`;
    $("file-list").innerHTML = "";
  }
}

// 切换文件面板浏览路径
function navFiles(newPath) {
  state.filesPath = newPath || "";
  loadFiles();
}

function renderFiles(data) {
  // 当前所在的"项目"= 路径第一段;空路径 → user_root,无项目上下文
  const segs = (data.current || "").split("/").filter(Boolean);
  const projName = segs[0] || "";
  // 名称过长时显示前 11 字符 + …,完整名留 title 提示(避免顶栏挤压"文件"换行)
  const projShort = projName.length > 12 ? projName.slice(0, 11) + "…" : projName;
  $("files-proj").textContent = projShort ? "· " + projShort : "· (根目录)";
  $("files-proj").title = projName || data.root || "";
  // crumbs root 标"我的"(user_root),更直观;其余原样
  const cr = data.crumbs.map((c, i) => {
    const label = i === 0 ? "我的" : c.label;
    const isLast = i === data.crumbs.length - 1;
    if (isLast) return `<span>${escapeHtml(label)}</span>`;
    return `<a href="#" data-rel="${escapeHtml(c.rel)}">${escapeHtml(label)}</a> /`;
  }).join(" ");
  $("file-crumbs").innerHTML = cr || `<span class="muted">/</span>`;
  $("file-crumbs").querySelectorAll("a").forEach((a) => {
    a.onclick = (e) => { e.preventDefault(); navFiles(a.dataset.rel); };
  });
  if (!data.exists) {
    $("file-list").innerHTML = `<div class="empty">(目录尚未创建)</div>`;
    state.entriesByRel = {};
    return;
  }
  if (!data.entries.length) {
    $("file-list").innerHTML = `<div class="empty">(空目录)</div>`;
    state.entriesByRel = {};
    return;
  }
  state.entriesByRel = {};
  for (const e of data.entries) state.entriesByRel[e.rel] = e;
  $("file-list").innerHTML = data.entries.map((e) => {
    const cls = e.is_dir ? "ico-dir" : "ico-file";
    const fullTitle = e.rel || e.name;
    return `
      <div class="file-row" data-rel="${escapeHtml(e.rel)}" title="${escapeHtml(fullTitle)}">
        <span class="${cls} name" data-rel="${escapeHtml(e.rel)}" data-isdir="${e.is_dir}" title="${escapeHtml(fullTitle)}">
          ${escapeHtml(e.name)}
        </span>
        <span class="size">${humanSize(e.size)}</span>
        <button class="dd-toggle file-menu" data-rel="${escapeHtml(e.rel)}" title="文件操作">⋯</button>
      </div>
    `;
  }).join("");
  $("file-list").querySelectorAll(".name").forEach((el) => {
    el.style.cursor = "pointer";
    el.onclick = () => {
      const rel = el.dataset.rel;
      if (el.dataset.isdir === "true") { navFiles(rel); }
      else { openFilePreview(rel); }
    };
  });
  $("file-list").querySelectorAll(".file-menu").forEach((btn) => {
    btn.onclick = (ev) => {
      ev.stopPropagation();
      const e = state.entriesByRel[btn.dataset.rel];
      if (!e) return;
      showMenu(btn, fileMenuItems(e));
    };
  });
}

function fileMenuItems(e) {
  const items = [
    { act: "rename", label: "重命名", cls: "act-rename",
      onclick: () => renameFile(e.rel, e.name, e.is_dir) },
  ];
  if (!e.is_dir) {
    items.push({ act: "download", label: "下载", cls: "act-download",
      onclick: () => downloadFile(e.rel) });
  }
  items.push({ act: "delete", label: "删除", cls: "act-delete",
    onclick: () => deleteFile(e.rel, e.name, e.is_dir) });
  return items;
}

async function deleteFile(rel, name, isDir) {
  let recursive = false;
  if (!isDir) {
    if (!await dialogConfirm({ title: "删除文件", message: `确认删除文件 "${name}"?`, okText: "删除", danger: true })) return;
  } else {
    // 探一下目录内容:空目录走普通 rmdir;非空才递归,二次确认显示条目数
    let entries;
    try {
      const data = await api("GET", "/v1/files?path=" + encodeURIComponent(rel));
      entries = data.entries || [];
    } catch (e) {
      if (e.status === 401) { logout(); return; }
      message("读目录失败:" + e.message, "error");
      return;
    }
    if (entries.length === 0) {
      if (!await dialogConfirm({ title: "删除目录", message: `确认删除空目录 "${name}"?`, okText: "删除", danger: true })) return;
    } else {
      const hasSub = entries.some((x) => x.is_dir);
      const tip = hasSub ? "(含子目录)" : "";
      if (!await dialogConfirm({
        title: "删除目录",
        message:
          `目录 "${name}" 含 ${entries.length} 项${tip},` +
          `将递归删除全部内容,不可恢复。\n` +
          `(若为顶层目录且仍被 task 引用,需先删 task)\n确认?`,
        okText: "递归删除", danger: true,
      })) return;
      recursive = true;
    }
  }
  try {
    await api("POST", "/v1/files/delete", { path: rel, recursive });
    await loadFiles();
    // 删的若是顶层目录,folders 列表也得跟着变;子级删除走这里也无副作用
    await loadFolderSuggestions();
  } catch (e) {
    if (e.status === 401) { logout(); return; }
    message("删除失败:" + e.message, "error");
  }
}

async function renameFile(rel, name, isDir) {
  const what = isDir ? "目录" : "文件";
  const newName = await dialogPrompt({
    title: `重命名${what}`, label: `${what}名称`, value: name, placeholder: `输入新的${what}名`,
  });
  if (newName == null) return;
  const trimmed = newName.trim();
  if (!trimmed || trimmed === name) return;
  try {
    const res = await api("POST", "/v1/files/rename", { path: rel, new_name: trimmed });
    // 面板若停在被改名的子树里,做前缀替换继续停留在等价位置
    if (state.filesPath === rel) {
      state.filesPath = res.new;
    } else if (state.filesPath && state.filesPath.startsWith(rel + "/")) {
      state.filesPath = res.new + state.filesPath.slice(rel.length);
    }
    await loadFolderSuggestions();
    // 顶层目录改名 → tasks_updated>0,任务列表 / 当前 task 头里的 working_dir 都得刷
    if (res && res.tasks_updated > 0) {
      await loadTaskList();
      if (state.taskId) { await selectTask(state.taskId); return; }
    }
    await loadFiles();
  } catch (e) {
    if (e.status === 401) { logout(); return; }
    message("重命名失败:" + e.message, "error");
  }
}

function uploadTotalBytes(files) {
  return (files || []).reduce((sum, f) => sum + (f.size || 0), 0);
}
function uploadFilesLabel(files) {
  if (!files || !files.length) return "";
  return files.length === 1 ? files[0].name : `${files[0].name} 等 ${files.length} 个文件`;
}
export function formatUploadProgress(files, loaded, total) {
  const denom = total || uploadTotalBytes(files);
  const pct = denom ? Math.min(100, Math.max(0, Math.round((loaded / denom) * 100))) : 0;
  const sizeText = denom ? ` · ${humanSize(Math.min(loaded, denom))}/${humanSize(denom)}` : "";
  return `上传中 ${pct}% · ${uploadFilesLabel(files)}${sizeText}`;
}
function setPaneUploadStatus(files, loaded, total) {
  const el = $("file-upload-status");
  const denom = total || uploadTotalBytes(files);
  const pct = denom ? Math.min(100, Math.max(0, Math.round((loaded / denom) * 100))) : 0;
  el.classList.add("show");
  el.innerHTML = `${escapeHtml(formatUploadProgress(files, loaded, total))}<div class="bar"><span style="width:${pct}%"></span></div>`;
}
function finishPaneUploadStatus(ok, files) {
  const el = $("file-upload-status");
  el.classList.add("show");
  el.innerHTML = ok
    ? `上传完成 · ${escapeHtml(uploadFilesLabel(files))}<div class="bar"><span style="width:100%"></span></div>`
    : `上传失败 · ${escapeHtml(uploadFilesLabel(files))}`;
  setTimeout(() => {
    if (el.textContent.startsWith(ok ? "上传完成" : "上传失败")) {
      el.classList.remove("show");
      el.innerHTML = "";
    }
  }, 3500);
}
async function uploadFilesWithPaneStatus(files) {
  if (!files || !files.length) return null;
  setPaneUploadStatus(files, 0, uploadTotalBytes(files));
  const saved = await uploadFiles(files, {
    onProgress: (loaded, total) => setPaneUploadStatus(files, loaded, total),
  });
  finishPaneUploadStatus(!!(saved && saved.length), files);
  return saved;
}

export async function uploadFiles(files, opts = {}) {
  if (!files || !files.length) return null;
  const fd = new FormData();
  fd.append("path", state.filesPath || "");
  for (const f of files) fd.append("files", f);
  try {
    const data = await new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("POST", "/v1/files/upload");
      xhr.setRequestHeader("Authorization", "Bearer " + state.token);
      xhr.upload.onprogress = (ev) => {
        if (opts.onProgress && ev.lengthComputable) opts.onProgress(ev.loaded, ev.total);
      };
      xhr.onerror = () => reject(new Error("网络错误,上传失败"));
      xhr.onload = () => {
        let payload = {};
        try { payload = xhr.responseText ? JSON.parse(xhr.responseText) : {}; }
        catch (_) { payload = {}; }
        if (xhr.status < 200 || xhr.status >= 300) {
          const err = new Error(payload.detail || (xhr.status + " 上传失败"));
          err.status = xhr.status;
          reject(err);
          return;
        }
        resolve(payload);
      };
      if (opts.onProgress) opts.onProgress(0, uploadTotalBytes(files));
      xhr.send(fd);
    });
    await loadFiles();
    return data.saved || [];
  } catch (e) {
    if (e.status === 401) { logout(); return null; }
    message("上传失败:" + e.message, "error");
    return null;
  }
}

async function uploadSelected() {
  const inp = $("upload-input");
  const files = Array.from(inp.files || []);
  try {
    await uploadFilesWithPaneStatus(files);
  } finally {
    inp.value = "";  // 允许重新选同名文件
  }
}
