const form = document.querySelector("#segmentForm");
const methodSelect = document.querySelector("#method");
const methodGrid = document.querySelector("#methodGrid");
const sensitivity = document.querySelector("#sensitivity");
const sensitivityValue = document.querySelector("#sensitivityValue");
const fileInput = document.querySelector("#file");
const fileName = document.querySelector("#fileName");
const dropZone = document.querySelector("#dropZone");
const sampleButton = document.querySelector("#sampleButton");
const clearButton = document.querySelector("#clearButton");
const resultGrid = document.querySelector("#resultGrid");
const emptyState = document.querySelector("#emptyState");
const videoLink = document.querySelector("#videoLink");
const health = document.querySelector("#health");
const template = document.querySelector("#resultCardTemplate");
const previewEmpty = document.querySelector("#previewEmpty");
const videoPreview = document.querySelector("#videoPreview");
const imagePreview = document.querySelector("#imagePreview");
const progressWrap = document.querySelector("#progressWrap");
const progressText = document.querySelector("#progressText");
const summaryStrip = document.querySelector("#summaryStrip");
const summaryJob = document.querySelector("#summaryJob");
const summaryFrames = document.querySelector("#summaryFrames");
const summaryCoverage = document.querySelector("#summaryCoverage");
const summarySkeleton = document.querySelector("#summarySkeleton");
const resultCount = document.querySelector("#resultCount");
const jobMeta = document.querySelector("#jobMeta");
const detailDialog = document.querySelector("#detailDialog");
const closeDialog = document.querySelector("#closeDialog");
const openSourceButton = document.querySelector("#openSourceButton");
const sourcePaneTitle = document.querySelector("#sourcePaneTitle");
const sourceDialog = document.querySelector("#sourceDialog");
const closeSourceDialog = document.querySelector("#closeSourceDialog");
const sourceTitle = document.querySelector("#sourceTitle");
const sourceVideo = document.querySelector("#sourceVideo");
const sourceImage = document.querySelector("#sourceImage");

const methodLabels = new Map();
const methodDescriptions = new Map();
let selectedFile = null;
let currentObjectUrl = null;
let lastFrames = [];

function setBusy(isBusy, text = "运行分割") {
  const button = form.querySelector(".primary");
  button.disabled = isBusy;
  button.querySelector("span").textContent = isBusy ? "分割中" : text;
  progressWrap.hidden = !isBusy;
  if (isBusy) {
    progressText.textContent = "正在上传、抽帧并执行导丝分割";
  }
}

function setFile(file) {
  selectedFile = file;
  if (typeof DataTransfer !== "undefined") {
    const transfer = new DataTransfer();
    transfer.items.add(file);
    fileInput.files = transfer.files;
  }
  fileName.textContent = `${file.name} · ${(file.size / 1024 / 1024).toFixed(2)} MB`;
  renderPreview(file);
}

function revokePreview() {
  if (currentObjectUrl) {
    URL.revokeObjectURL(currentObjectUrl);
    currentObjectUrl = null;
  }
}

function renderPreview(file) {
  revokePreview();
  currentObjectUrl = URL.createObjectURL(file);
  previewEmpty.hidden = true;
  videoPreview.hidden = true;
  imagePreview.hidden = true;
  openSourceButton.hidden = false;
  if (file.type.startsWith("video/")) {
    videoPreview.src = currentObjectUrl;
    videoPreview.hidden = false;
    openSourceButton.textContent = "放大查看";
    sourcePaneTitle.textContent = "查看原始视频";
    sourceTitle.textContent = "原始视频";
  } else {
    imagePreview.src = currentObjectUrl;
    imagePreview.hidden = false;
    openSourceButton.textContent = "放大查看";
    sourcePaneTitle.textContent = "查看原始图像";
    sourceTitle.textContent = "原始图像";
  }
  jobMeta.textContent = `已选择 ${file.name}`;
}

async function loadHealth() {
  try {
    const response = await fetch("/api/health");
    if (!response.ok) throw new Error("bad health");
    const data = await response.json();
    health.textContent = `${data.service} ${data.version}`;
    health.classList.add("ok");
  } catch {
    health.textContent = "服务不可用";
    health.classList.add("bad");
  }
}

function activateMethod(key) {
  methodSelect.value = key;
  [...methodGrid.querySelectorAll(".method-option")].forEach((node) => {
    node.classList.toggle("is-active", node.dataset.method === key);
  });
}

async function loadMethods() {
  const response = await fetch("/api/methods");
  const data = await response.json();
  methodSelect.innerHTML = "";
  methodGrid.innerHTML = "";
  Object.entries(data.methods).forEach(([key, value]) => {
    methodLabels.set(key, value.label);
    methodDescriptions.set(key, value.description);

    const option = document.createElement("option");
    option.value = key;
    option.textContent = value.label;
    methodSelect.appendChild(option);

    const card = document.createElement("button");
    card.type = "button";
    card.className = "method-option";
    card.dataset.method = key;
    card.innerHTML = `<strong>${value.label}</strong><span>${value.description}</span>`;
    card.addEventListener("click", () => activateMethod(key));
    methodGrid.appendChild(card);
  });
  activateMethod("fusion");
}

function updateSummary(data) {
  const frames = data.frames || [];
  const coverage = frames.reduce((sum, frame) => sum + frame.metrics.coverage, 0) / Math.max(1, frames.length);
  const skeleton = frames.reduce((sum, frame) => sum + frame.metrics.skeleton_length, 0) / Math.max(1, frames.length);
  summaryJob.textContent = data.job_id;
  summaryFrames.textContent = frames.length;
  summaryCoverage.textContent = `${(coverage * 100).toFixed(3)}%`;
  summarySkeleton.textContent = Math.round(skeleton);
  summaryStrip.hidden = false;
  resultCount.textContent = `${frames.length} 个结果`;
  jobMeta.textContent = `${data.kind === "video" ? "视频" : "图像"} · ${methodLabels.get(data.method) || data.method}`;
}

function openDetail(frame) {
  document.querySelector("#detailMethod").textContent = methodLabels.get(frame.method) || frame.method;
  document.querySelector("#detailTitle").textContent = `帧 ${frame.frame_index} 分割详情`;
  document.querySelector("#detailOriginal").src = frame.original_url;
  document.querySelector("#detailOverlay").src = frame.overlay_url;
  document.querySelector("#detailMask").src = frame.mask_url;
  const metrics = document.querySelector("#detailMetrics");
  metrics.innerHTML = `
    <div><dt>覆盖率</dt><dd>${(frame.metrics.coverage * 100).toFixed(3)}%</dd></div>
    <div><dt>掩膜像素</dt><dd>${frame.metrics.mask_pixels}</dd></div>
    <div><dt>骨架长度</dt><dd>${frame.metrics.skeleton_length}</dd></div>
    <div><dt>连通域</dt><dd>${frame.metrics.components}</dd></div>
  `;
  detailDialog.showModal();
}

function renderResults(data) {
  lastFrames = data.frames || [];
  resultGrid.innerHTML = "";
  emptyState.hidden = lastFrames.length > 0;
  videoLink.hidden = !data.video_url;
  if (data.video_url) {
    videoLink.href = data.video_url;
    videoLink.setAttribute("download", "");
  }
  updateSummary(data);

  lastFrames.forEach((frame) => {
    const node = template.content.firstElementChild.cloneNode(true);
    node.querySelector(".method").textContent = methodLabels.get(frame.method) || frame.method;
    node.querySelector(".frame-index").textContent = `帧 ${frame.frame_index}`;
    node.querySelector(".overlay").src = frame.overlay_url;
    node.querySelector(".coverage").textContent = `${(frame.metrics.coverage * 100).toFixed(3)}%`;
    node.querySelector(".skeleton").textContent = frame.metrics.skeleton_length;
    node.querySelector(".components").textContent = frame.metrics.components;
    node.addEventListener("click", () => openDetail(frame));
    node.addEventListener("keydown", (event) => {
      if (event.key === "Enter") openDetail(frame);
    });
    resultGrid.appendChild(node);
  });
}

async function loadSample() {
  sampleButton.disabled = true;
  sampleButton.textContent = "加载中";
  emptyState.hidden = false;
  emptyState.textContent = "正在加载内置样例视频。";
  try {
    const response = await fetch("/api/samples");
    if (!response.ok) throw new Error("样例清单读取失败");
    const data = await response.json();
    const sample = data.samples.find((item) => item.kind === "video") || data.samples[0];
    if (!sample) throw new Error("未找到样例文件");
    const blobResponse = await fetch(sample.url);
    if (!blobResponse.ok) throw new Error("样例文件下载失败");
    const blob = await blobResponse.blob();
    const file = new File([blob], sample.name, { type: sample.kind === "video" ? "video/mp4" : "image/png" });
    setFile(file);
    emptyState.textContent = "样例已加载。点击左侧“运行分割”即可生成导丝掩膜和叠加结果。";
  } catch (error) {
    emptyState.textContent = error.message || "样例加载失败";
  } finally {
    sampleButton.disabled = false;
    sampleButton.textContent = "加载样例";
  }
}

function clearAll() {
  selectedFile = null;
  fileInput.value = "";
  fileName.textContent = "支持 mp4、avi、png、jpg、tiff";
  revokePreview();
  videoPreview.removeAttribute("src");
  imagePreview.removeAttribute("src");
  videoPreview.hidden = true;
  imagePreview.hidden = true;
  sourceVideo.hidden = true;
  sourceImage.hidden = true;
  sourceVideo.removeAttribute("src");
  sourceImage.removeAttribute("src");
  openSourceButton.hidden = true;
  sourcePaneTitle.textContent = "查看原始视频";
  if (sourceDialog.open) sourceDialog.close();
  previewEmpty.hidden = false;
  resultGrid.innerHTML = "";
  emptyState.hidden = false;
  summaryStrip.hidden = true;
  videoLink.hidden = true;
  resultCount.textContent = "0 个结果";
  jobMeta.textContent = "等待输入";
}

sensitivity.addEventListener("input", () => {
  sensitivityValue.textContent = Number(sensitivity.value).toFixed(2);
});

fileInput.addEventListener("change", () => {
  const file = fileInput.files[0];
  if (file) setFile(file);
});

["dragenter", "dragover"].forEach((eventName) => {
  dropZone.addEventListener(eventName, (event) => {
    event.preventDefault();
    dropZone.classList.add("is-dragging");
  });
});

["dragleave", "drop"].forEach((eventName) => {
  dropZone.addEventListener(eventName, (event) => {
    event.preventDefault();
    dropZone.classList.remove("is-dragging");
  });
});

dropZone.addEventListener("drop", (event) => {
  const file = event.dataTransfer.files[0];
  if (file) setFile(file);
});

sampleButton.addEventListener("click", loadSample);
clearButton.addEventListener("click", clearAll);
closeDialog.addEventListener("click", () => detailDialog.close());
closeSourceDialog.addEventListener("click", () => sourceDialog.close());
openSourceButton.addEventListener("click", () => {
  if (!selectedFile || !currentObjectUrl) return;
  sourceVideo.hidden = true;
  sourceImage.hidden = true;
  if (selectedFile.type.startsWith("video/")) {
    sourceVideo.src = currentObjectUrl;
    sourceVideo.hidden = false;
  } else {
    sourceImage.src = currentObjectUrl;
    sourceImage.hidden = false;
  }
  sourceDialog.showModal();
});

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const file = fileInput.files[0] || selectedFile;
  if (!file) {
    emptyState.hidden = false;
    emptyState.textContent = "请先选择文件或加载样例。";
    return;
  }

  setBusy(true);
  emptyState.hidden = false;
  emptyState.textContent = "正在抽帧和分割，请稍候。";
  resultGrid.innerHTML = "";
  videoLink.hidden = true;

  try {
    const payload = new FormData(form);
    payload.set("file", file);
    const response = await fetch("/api/segment", {
      method: "POST",
      body: payload,
    });
    const data = await response.json();
    if (!response.ok) {
      throw new Error(data.detail || "分割失败");
    }
    renderResults(data);
  } catch (error) {
    emptyState.hidden = false;
    emptyState.textContent = error.message;
  } finally {
    setBusy(false);
  }
});

loadHealth();
loadMethods();
