(() => {
  const ACTIVE_STATUSES = new Set(["running", "retrying", "pausing", "paused", "cancelling"]);
  const jobsBody = document.getElementById("jobs");
  const statusText = document.getElementById("statusText");
  const manualUrl = document.getElementById("manualUrl");
  const manualStart = document.getElementById("manualStart");
  const refreshButton = document.getElementById("refresh");
  const tabButtons = Array.from(document.querySelectorAll(".tabs button[data-filter]"));

  let currentFilter = "active";
  let jobs = [];
  let bridgeError = "";

  manualStart.addEventListener("click", addManualDownload);
  refreshButton.addEventListener("click", loadJobs);
  manualUrl.addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      event.preventDefault();
      addManualDownload();
    }
  });
  jobsBody.addEventListener("click", onJobAction);
  for (const button of tabButtons) {
    button.addEventListener("click", () => setFilter(button.dataset.filter));
  }

  loadJobs();
  window.setInterval(loadJobs, 1500);

  function setFilter(filter) {
    currentFilter = filter || "active";
    for (const button of tabButtons) {
      button.classList.toggle("is-active", button.dataset.filter === currentFilter);
    }
    render();
  }

  function addManualDownload() {
    const url = normalizeDownloadUrl(manualUrl.value);
    if (!url) {
      statusText.textContent = "URL 오류";
      return;
    }

    manualStart.disabled = true;
    chrome.runtime.sendMessage({ type: "accelerate-download-url", url }, (response) => {
      manualStart.disabled = false;
      if (chrome.runtime.lastError || !response?.ok) {
        statusText.textContent = response?.error || chrome.runtime.lastError?.message || "추가 실패";
        return;
      }

      manualUrl.value = "";
      statusText.textContent = "시작 창 열림";
      loadJobs();
    });
  }

  function loadJobs() {
    chrome.runtime.sendMessage({ type: "get-download-manager-jobs" }, (response) => {
      if (chrome.runtime.lastError || !response?.ok) {
        statusText.textContent = response?.error || chrome.runtime.lastError?.message || "브릿지 확인 실패";
        return;
      }

      jobs = Array.isArray(response.jobs) ? response.jobs : [];
      bridgeError = response.bridgeOk === false ? response.bridgeError || "브릿지 오프라인" : "";
      render();
    });
  }

  function render() {
    const visibleJobs = jobs.filter(matchesFilter);
    const summary = summaryText(jobs);
    statusText.textContent = bridgeError ? `${summary} · 브릿지 오프라인` : summary;
    statusText.title = bridgeError;
    if (!visibleJobs.length) {
      jobsBody.replaceChildren(emptyRow());
      return;
    }

    jobsBody.replaceChildren(...visibleJobs.map(createJobRow));
  }

  function matchesFilter(job) {
    if (currentFilter === "all") {
      return true;
    }
    if (currentFilter === "done") {
      return job.status === "done";
    }
    if (currentFilter === "queued") {
      return isQueuedJob(job);
    }
    return ACTIVE_STATUSES.has(job.status || "");
  }

  function createJobRow(job) {
    const row = document.createElement("tr");
    row.dataset.id = job.id || "";

    row.append(
      fileCell(job),
      statusCell(job),
      textCell(sizeText(job)),
      textCell(speedText(job)),
      textCell(etaText(job)),
      actionCell(job)
    );
    return row;
  }

  function fileCell(job) {
    const cell = document.createElement("td");
    const name = div(job.filename || fileNameFromPath(job.file) || fileNameFromUrl(job.url) || "다운로드", "name");
    const path = div(job.file || job.url || "", "path");
    name.title = name.textContent;
    path.title = path.textContent;
    cell.append(name, path);
    return cell;
  }

  function statusCell(job) {
    const cell = document.createElement("td");
    const percent = progressPercent(job);
    const line = div(statusTextForJob(job, percent), `status-line status-${job.status || "unknown"}`);
    const meta = div(metaText(job), "meta");
    const progress = document.createElement("div");
    const fill = document.createElement("span");
    progress.className = "progress";
    fill.style.width = `${percent}%`;
    progress.append(fill);
    cell.append(line, meta, progress);
    return cell;
  }

  function actionCell(job) {
    const cell = document.createElement("td");
    const actions = document.createElement("div");
    actions.className = "actions";
    for (const action of actionsForJob(job)) {
      const button = document.createElement("button");
      button.type = "button";
      button.dataset.action = action;
      button.textContent = actionLabel(action);
      actions.append(button);
    }
    cell.append(actions);
    return cell;
  }

  function onJobAction(event) {
    const button = event.target.closest("button[data-action]");
    const row = event.target.closest("tr[data-id]");
    if (!button || !row?.dataset.id) {
      return;
    }

    button.disabled = true;
    chrome.runtime.sendMessage(
      { type: "control-download-job", jobId: row.dataset.id, action: button.dataset.action },
      (response) => {
        button.disabled = false;
        if (chrome.runtime.lastError || !response?.ok) {
          statusText.textContent = response?.error || chrome.runtime.lastError?.message || "작업 실패";
          return;
        }
        loadJobs();
      }
    );
  }

  function actionsForJob(job) {
    if (job.queuedLocal) {
      return ["start", "remove"];
    }

    const status = job.status || "";
    if (["running", "queued", "retrying", "pausing"].includes(status)) {
      return ["pause", "cancel"];
    }
    if (status === "paused") {
      return ["resume", "cancel"];
    }
    if (status === "done") {
      return ["open", "folder", "clear"];
    }
    if (["failed", "cancelled"].includes(status)) {
      return ["retry", "folder", "clear"];
    }
    return ["clear"];
  }

  function actionLabel(action) {
    return {
      pause: "일시정지",
      resume: "재개",
      retry: "다시받기",
      cancel: "취소",
      clear: "삭제",
      start: "시작",
      remove: "제거",
      open: "열기",
      folder: "폴더"
    }[action] || action;
  }

  function statusLabel(job) {
    if (isQueuedJob(job)) {
      return "예약";
    }

    return {
      queued: "대기",
      running: "진행",
      retrying: "재시도",
      pausing: "정지 중",
      paused: "일시정지",
      cancelling: "취소 중",
      cancelled: "취소됨",
      done: "완료",
      failed: "실패"
    }[job.status] || job.status || "상태 없음";
  }

  function statusTextForJob(job, percent) {
    const total = Number(job.total || 0);
    if (total > 0 || job.status === "done") {
      return `${statusLabel(job)} · ${percent}%`;
    }

    return statusLabel(job);
  }

  function metaText(job) {
    const mode = job.mode === "accelerated"
      ? `${Number(job.connections || 0) || 1}연결`
      : job.mode === "stream"
        ? "스트림 병합"
        : job.mode === "single"
          ? "단일 연결"
          : "";
    const source = sourceLabel(job.source);
    return [mode, chunkText(job), job.qualityLabel || "", source, job.error || ""].filter(Boolean).join(" · ");
  }

  function chunkText(job) {
    const total = Number(job.partsTotal || 0);
    if (job.mode !== "accelerated" || total <= 1) {
      return "";
    }
    const done = Math.max(0, Math.min(total, Number(job.partsDone || 0)));
    return `청크 ${done}/${total}`;
  }

  function sourceLabel(source) {
    return {
      "manual-url": "직접 입력",
      "link-click": "링크",
      "chrome-download": "Chrome 다운로드",
      "video-direct": "영상 원본",
      "video-hls": "영상 HLS",
      "video-dash": "영상 DASH"
    }[source] || source || "";
  }

  function progressPercent(job) {
    const total = Number(job.total || 0);
    const downloaded = Number(job.downloaded || 0);
    if (total <= 0) {
      return job.status === "done" ? 100 : 0;
    }
    return Math.max(0, Math.min(100, Math.floor((downloaded / total) * 100)));
  }

  function sizeText(job) {
    const total = Number(job.total || 0);
    const downloaded = Number(job.downloaded || 0);
    if (total > 0) {
      return `${formatBytes(downloaded)} / ${formatBytes(total)}`;
    }
    return downloaded > 0 ? formatBytes(downloaded) : "-";
  }

  function speedText(job) {
    const speed = Number(job.speedBytesPerSec || 0);
    return speed > 0 && ACTIVE_STATUSES.has(job.status || "") ? `${formatBytes(speed)}/s` : "-";
  }

  function etaText(job) {
    const eta = Number(job.etaSeconds || 0);
    return eta > 0 && ACTIVE_STATUSES.has(job.status || "") ? formatDuration(eta) : "-";
  }

  function summaryText(items) {
    const active = items.filter((job) => ACTIVE_STATUSES.has(job.status || "")).length;
    const queued = items.filter(isQueuedJob).length;
    const done = items.filter((job) => job.status === "done").length;
    const failed = items.filter((job) => ["failed", "cancelled"].includes(job.status || "")).length;
    return `진행 ${active} · 예약 ${queued} · 완료 ${done} · 실패/취소 ${failed}`;
  }

  function isQueuedJob(job) {
    return Boolean(job.queuedLocal || job.status === "queued");
  }

  function textCell(text) {
    const cell = document.createElement("td");
    cell.textContent = text;
    cell.title = text;
    return cell;
  }

  function div(text, className) {
    const element = document.createElement("div");
    element.className = className;
    element.textContent = text;
    return element;
  }

  function emptyRow() {
    const row = document.createElement("tr");
    const cell = document.createElement("td");
    cell.colSpan = 6;
    cell.className = "empty";
    cell.textContent = "표시할 다운로드가 없습니다.";
    row.append(cell);
    return row;
  }

  function normalizeDownloadUrl(value) {
    const trimmed = String(value || "").trim();
    if (!trimmed) {
      return "";
    }
    const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
    try {
      const url = new URL(withScheme);
      return ["http:", "https:"].includes(url.protocol) ? url.href : "";
    } catch (_) {
      return "";
    }
  }

  function fileNameFromPath(value) {
    return String(value || "").split(/[\\/]/).filter(Boolean).pop() || "";
  }

  function fileNameFromUrl(value) {
    try {
      return decodeURIComponent(new URL(value || "").pathname.split("/").filter(Boolean).pop() || "");
    } catch (_) {
      return "";
    }
  }

  function formatBytes(value) {
    const number = Number(value || 0);
    if (number >= 1024 * 1024 * 1024) {
      return `${(number / 1024 / 1024 / 1024).toFixed(2)} GB`;
    }
    if (number >= 1024 * 1024) {
      return `${(number / 1024 / 1024).toFixed(1)} MB`;
    }
    if (number >= 1024) {
      return `${(number / 1024).toFixed(1)} KB`;
    }
    return `${Math.round(number)} B`;
  }

  function formatDuration(seconds) {
    const value = Math.max(0, Math.round(Number(seconds || 0)));
    const minutes = Math.floor(value / 60);
    const restSeconds = value % 60;
    if (minutes >= 60) {
      const hours = Math.floor(minutes / 60);
      return `${hours}시간 ${minutes % 60}분`;
    }
    if (minutes > 0) {
      return `${minutes}분 ${restSeconds}초`;
    }
    return `${restSeconds}초`;
  }
})();
