const TASK_LANES = ["backlog", "committed", "in-progress", "blocked", "done", "canceled"];

const TASK_ACTION_LABELS = {
  backlog: "Backlog",
  committed: "Commit",
  "in-progress": "Start",
  blocked: "Block",
  done: "Done",
  canceled: "Cancel",
};

const TASK_LANE_LABELS = {
  backlog: "Backlog",
  committed: "Committed",
  "in-progress": "In Progress",
  blocked: "Blocked",
  done: "Done",
  canceled: "Canceled",
};

const TASK_LANE_THEMES = {
  backlog: {
    accent: "#5f7884",
    accentSoft: "rgba(95, 120, 132, 0.13)",
    muted: "#6b7e87",
    buttonInk: "#294a57",
  },
  committed: {
    accent: "#8a6946",
    accentSoft: "rgba(138, 105, 70, 0.14)",
    muted: "#7f664a",
    buttonInk: "#5a3b19",
  },
  "in-progress": {
    accent: "#4f7862",
    accentSoft: "rgba(79, 120, 98, 0.13)",
    muted: "#5e7768",
    buttonInk: "#214437",
  },
  blocked: {
    accent: "#a55f4b",
    accentSoft: "rgba(165, 95, 75, 0.13)",
    muted: "#906659",
    buttonInk: "#6c2f21",
  },
  done: {
    accent: "#6d7f58",
    accentSoft: "rgba(109, 127, 88, 0.12)",
    muted: "#6b755d",
    buttonInk: "#304121",
  },
  canceled: {
    accent: "#7b716a",
    accentSoft: "rgba(123, 113, 106, 0.12)",
    muted: "#7b716a",
    buttonInk: "#433932",
  },
};

function isTaskLane(value) {
  return typeof value === "string" && TASK_LANES.includes(value);
}

function normalizeTag(raw) {
  const trimmed = String(raw || "")
    .trim()
    .replace(/^#+/, "")
    .replace(/\s+/g, "-");
  return trimmed ? `#${trimmed}` : "";
}

function normalizeTags(raw) {
  if (!Array.isArray(raw)) return [];
  const seen = new Set();
  const tags = [];
  for (const value of raw) {
    const tag = normalizeTag(value);
    const key = tag.toLowerCase();
    if (!tag || seen.has(key)) continue;
    seen.add(key);
    tags.push(tag);
  }
  return tags;
}

function normalizeMetadata(raw) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
  const entries = Object.entries(raw).filter(([, value]) => value !== undefined);
  return Object.fromEntries(entries);
}

function normalizeTask(raw, fallbackTitle) {
  const record = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
  const lane = isTaskLane(record.lane) ? record.lane : "backlog";
  return {
    taskPath: typeof record.task_path === "string" ? record.task_path.trim() : "",
    taskKey: typeof record.task_key === "string" ? record.task_key.trim() : "",
    title:
      typeof record.title === "string" && record.title.trim()
        ? record.title.trim()
        : fallbackTitle || "(Untitled task)",
    lane,
    created: typeof record.created === "string" ? record.created.trim() : "",
    updated: typeof record.updated === "string" ? record.updated.trim() : "",
    due: typeof record.due === "string" ? record.due.trim() : "",
    tags: normalizeTags(record.tags),
    body: typeof record.body === "string" ? record.body : "",
    metadata: normalizeMetadata(record.metadata),
  };
}

function normalizeTaskFromPayload(raw, fallback) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return fallback;
  return {
    taskPath: typeof raw.path === "string" ? raw.path.trim() : fallback.taskPath,
    taskKey: fallback.taskKey,
    title:
      typeof raw.title === "string" && raw.title.trim()
        ? raw.title.trim()
        : fallback.title,
    lane: isTaskLane(raw.lane) ? raw.lane : fallback.lane,
    created: typeof raw.created === "string" ? raw.created.trim() : fallback.created,
    updated: typeof raw.updated === "string" ? raw.updated.trim() : fallback.updated,
    due: typeof raw.due === "string" ? raw.due.trim() : fallback.due,
    tags: normalizeTags(raw.tags),
    body: typeof raw.body === "string" ? raw.body : fallback.body,
    metadata: normalizeMetadata(raw.metadata),
  };
}

function parseToolPayload(result) {
  if (result && typeof result === "object" && result.parsed && typeof result.parsed === "object") {
    return result.parsed;
  }
  const raw = typeof result?.content === "string" ? result.content : "";
  if (!raw.trim()) return null;
  try {
    const parsed = JSON.parse(raw);
    return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
  } catch {
    return null;
  }
}

function dueScore(hoursUntilDue) {
  if (hoursUntilDue <= 0) return 100;
  if (hoursUntilDue <= 6) return 96;
  if (hoursUntilDue <= 24) return 92;
  if (hoursUntilDue <= 72) return 82;
  if (hoursUntilDue <= 168) return 72;
  return 62;
}

function ageScore(ageDays) {
  if (ageDays >= 30) return 80;
  if (ageDays >= 21) return 76;
  if (ageDays >= 14) return 72;
  if (ageDays >= 7) return 68;
  if (ageDays >= 3) return 62;
  if (ageDays >= 1) return 58;
  return 54;
}

function computeTaskScore(task) {
  const now = Date.now();
  const rawDue = task.due ? (task.due.includes("T") ? task.due : `${task.due}T12:00:00`) : "";
  const dueMs = rawDue ? new Date(rawDue).getTime() : Number.NaN;
  let score = 54;
  if (Number.isFinite(dueMs)) {
    score = dueScore((dueMs - now) / (60 * 60 * 1000));
  } else {
    const createdMs = task.created ? new Date(task.created).getTime() : Number.NaN;
    if (Number.isFinite(createdMs)) {
      score = ageScore(Math.max(0, (now - createdMs) / (24 * 60 * 60 * 1000)));
    }
  }
  if (task.lane === "committed") return Math.min(100, score + 1);
  if (task.lane === "blocked") return Math.min(100, score + 4);
  if (task.lane === "in-progress") return Math.min(100, score + 2);
  return score;
}

function summarizeTaskBody(task) {
  const trimmed = String(task.body || "").trim();
  if (!trimmed || /^##\s+Imported\b/i.test(trimmed)) return "";
  return trimmed;
}

function renderTaskBodyMarkdown(host, body) {
  if (!body) return "";
  return body
    .replace(/\r\n?/g, "\n")
    .trim()
    .split("\n")
    .map((line) => {
      const trimmed = line.trim();
      if (!trimmed) return '<span class="task-card-ui__md-break" aria-hidden="true"></span>';

      let className = "task-card-ui__md-line";
      let content = trimmed;
      let prefix = "";

      const headingMatch = trimmed.match(/^(#{1,6})\s+(.*)$/);
      if (headingMatch) {
        className += " task-card-ui__md-line--heading";
        content = headingMatch[2];
      } else if (/^[-*]\s+/.test(trimmed)) {
        className += " task-card-ui__md-line--bullet";
        content = trimmed.replace(/^[-*]\s+/, "");
        prefix = "\u2022 ";
      } else if (/^\d+\.\s+/.test(trimmed)) {
        className += " task-card-ui__md-line--bullet";
        content = trimmed.replace(/^\d+\.\s+/, "");
        prefix = "\u2022 ";
      } else if (/^>\s+/.test(trimmed)) {
        className += " task-card-ui__md-line--quote";
        content = trimmed.replace(/^>\s+/, "");
        prefix = "> ";
      }

      const html = host.renderMarkdown(content, { inline: true });
      return `<span class="${className}">${
        prefix ? `<span class="task-card-ui__md-prefix">${prefix}</span>` : ""
      }${html}</span>`;
    })
    .join("");
}

function formatTaskDue(task) {
  if (!task.due) return "";
  const raw = task.due.includes("T") ? task.due : `${task.due}T00:00:00`;
  const parsed = new Date(raw);
  if (Number.isNaN(parsed.getTime())) return task.due;
  if (task.due.includes("T")) {
    const label = parsed.toLocaleString([], {
      month: "short",
      day: "numeric",
      hour: "numeric",
      minute: "2-digit",
    });
    return label.replace(/\s([AP]M)$/i, "$1");
  }
  return parsed.toLocaleDateString([], { month: "short", day: "numeric" });
}

function taskMoveOptions(lane) {
  return TASK_LANES.filter((targetLane) => targetLane !== lane).map((targetLane) => ({
    lane: targetLane,
    label: TASK_ACTION_LABELS[targetLane],
  }));
}

function taskLiveContent(task, errorText) {
  return {
    kind: "file_task",
    exists: true,
    task_path: task.taskPath || null,
    task_key: task.taskKey || null,
    title: task.title || null,
    lane: task.lane,
    created: task.created || null,
    updated: task.updated || null,
    due: task.due || null,
    tags: task.tags,
    metadata: task.metadata,
    score: computeTaskScore(task),
    status: task.lane,
    error: errorText || null,
  };
}

function autosizeEditor(editor) {
  editor.style.height = "0px";
  editor.style.height = `${Math.max(editor.scrollHeight, 20)}px`;
}

export function mount({ root, item, state, host }) {
  const cardEl = root.querySelector(".task-card-ui");
  const laneToggleEl = root.querySelector(".task-card-ui__lane-button");
  const laneWrapEl = root.querySelector(".task-card-ui__lane-wrap");
  const laneMenuEl = root.querySelector(".task-card-ui__lane-menu");
  const statusEl = root.querySelector(".task-card-ui__status");
  const titleEl = root.querySelector(".task-card-ui__title-slot");
  const tagsEl = root.querySelector(".task-card-ui__tags");
  const bodyEl = root.querySelector(".task-card-ui__body-slot");
  const metaEl = root.querySelector(".task-card-ui__meta");
  const dueEl = root.querySelector(".task-card-ui__chip");

  if (
    !(cardEl instanceof HTMLElement) ||
    !(laneToggleEl instanceof HTMLButtonElement) ||
    !(laneWrapEl instanceof HTMLElement) ||
    !(laneMenuEl instanceof HTMLElement) ||
    !(statusEl instanceof HTMLElement) ||
    !(titleEl instanceof HTMLElement) ||
    !(tagsEl instanceof HTMLElement) ||
    !(bodyEl instanceof HTMLElement) ||
    !(metaEl instanceof HTMLElement) ||
    !(dueEl instanceof HTMLElement)
  ) {
    return;
  }

  let task = normalizeTask(state, item.title);
  let busy = false;
  let errorText = "";
  let statusLabel = "";
  let statusKind = "neutral";
  let laneMenuOpen = false;
  let editingField = null;
  let holdTimer = null;

  const clearHoldTimer = () => {
    if (holdTimer !== null) {
      window.clearTimeout(holdTimer);
      holdTimer = null;
    }
  };

  const setStatus = (label, kind) => {
    statusLabel = label || "";
    statusKind = kind || "neutral";
    statusEl.textContent = statusLabel;
    statusEl.className = `task-card-ui__status${statusKind === "error" ? " is-error" : ""}`;
  };

  const publishLiveContent = () => {
    host.setLiveContent(taskLiveContent(task, errorText));
  };

  const closeLaneMenu = () => {
    laneMenuOpen = false;
    laneToggleEl.setAttribute("aria-expanded", "false");
    laneMenuEl.style.display = "none";
  };

  const openLaneMenu = () => {
    if (busy || !task.taskPath) return;
    laneMenuOpen = true;
    laneToggleEl.setAttribute("aria-expanded", "true");
    laneMenuEl.style.display = "flex";
  };

  const refreshFeed = () => {
    closeLaneMenu();
    host.requestFeedRefresh();
  };

  const setBusy = (nextBusy) => {
    busy = !!nextBusy;
    laneToggleEl.disabled = busy || !task.taskPath;
    titleEl.style.pointerEvents = busy ? "none" : "";
    bodyEl.style.pointerEvents = busy ? "none" : "";
    tagsEl
      .querySelectorAll("button")
      .forEach((button) => (button.disabled = busy || (!task.taskPath && button.dataset.role !== "noop")));
    laneMenuEl.querySelectorAll("button").forEach((button) => {
      button.disabled = busy;
    });
  };

  const runBusyAction = async (action) => {
    setBusy(true);
    errorText = "";
    setStatus("Saving", "neutral");
    try {
      await action();
      setStatus("", "neutral");
    } catch (error) {
      console.error("Task card action failed", error);
      errorText = error instanceof Error ? error.message : String(error);
      setStatus("Unavailable", "error");
    } finally {
      setBusy(false);
      render();
    }
  };

  const callTaskBoard = async (argumentsValue) => {
    const result = await host.callTool("task_board", argumentsValue);
    const payload = parseToolPayload(result);
    if (payload && typeof payload.error === "string" && payload.error.trim()) {
      throw new Error(payload.error);
    }
    return payload;
  };

  const moveTask = async (lane) =>
    runBusyAction(async () => {
      const payload = await callTaskBoard({ action: "move", task: task.taskPath, lane });
      const nextTask = normalizeTaskFromPayload(payload?.task, {
        ...task,
        lane,
        taskPath: typeof payload?.task_path === "string" ? payload.task_path.trim() : task.taskPath,
        updated: new Date().toISOString(),
      });
      task = nextTask;
      refreshFeed();
    });

  const editField = async (field, rawValue) => {
    const nextValue = rawValue.trim();
    const currentValue = field === "title" ? task.title : task.body;
    if (field === "title" && !nextValue) return false;
    if (nextValue === currentValue) {
      editingField = null;
      render();
      return true;
    }
    await runBusyAction(async () => {
      const payload = await callTaskBoard({
        action: "edit",
        task: task.taskPath,
        ...(field === "title" ? { title: nextValue } : { description: nextValue }),
      });
      task = normalizeTaskFromPayload(payload?.task, {
        ...task,
        ...(field === "title" ? { title: nextValue } : { body: nextValue }),
      });
      editingField = null;
      refreshFeed();
    });
    return true;
  };

  const addTag = async () => {
    const raw = window.prompt("Add tag to task", "");
    const tag = raw == null ? "" : normalizeTag(raw);
    if (!tag) return;
    await runBusyAction(async () => {
      const payload = await callTaskBoard({
        action: "add_tag",
        task: task.taskPath,
        tags: [tag],
      });
      task = normalizeTaskFromPayload(payload?.task, {
        ...task,
        tags: Array.from(new Set([...task.tags, tag])),
      });
      refreshFeed();
    });
  };

  const removeTag = async (tag) =>
    runBusyAction(async () => {
      const payload = await callTaskBoard({
        action: "remove_tag",
        task: task.taskPath,
        tags: [tag],
      });
      task = normalizeTaskFromPayload(payload?.task, {
        ...task,
        tags: task.tags.filter((value) => value !== tag),
      });
      refreshFeed();
    });

  const beginTitleEdit = () => {
    if (!task.taskPath || busy || editingField) return;
    closeLaneMenu();
    editingField = "title";
    render();
  };

  const beginBodyEdit = () => {
    if (!task.taskPath || busy || editingField) return;
    closeLaneMenu();
    editingField = "body";
    render();
  };

  const renderInlineEditor = (targetEl, field, value, placeholder) => {
    targetEl.innerHTML = "";
    const editor = document.createElement("textarea");
    editor.className = `${field === "title" ? "task-card-ui__title" : "task-card-ui__body"} task-card-ui__editor`;
    editor.rows = field === "title" ? 1 : Math.max(1, value.split("\n").length);
    editor.value = value;
    if (placeholder) editor.placeholder = placeholder;
    editor.disabled = busy;
    targetEl.appendChild(editor);
    autosizeEditor(editor);

    const cancel = () => {
      editingField = null;
      render();
    };

    editor.addEventListener("input", () => {
      autosizeEditor(editor);
    });

    editor.addEventListener("keydown", (event) => {
      if (event.key === "Escape") {
        event.preventDefault();
        cancel();
        return;
      }
      if (field === "title" && event.key === "Enter" && !event.shiftKey) {
        event.preventDefault();
        editor.blur();
        return;
      }
      if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
        event.preventDefault();
        editor.blur();
      }
    });

    editor.addEventListener("blur", () => {
      if (editingField !== field) return;
      void editField(field, editor.value);
    });

    requestAnimationFrame(() => {
      editor.focus();
      const end = editor.value.length;
      editor.setSelectionRange(end, end);
    });
  };

  const renderTags = () => {
    tagsEl.innerHTML = "";
    task.tags.forEach((tag) => {
      const button = document.createElement("button");
      button.className = "task-card-ui__tag";
      button.type = "button";
      button.textContent = tag;
      button.title = `Hold to remove ${tag}`;
      button.disabled = busy;
      button.addEventListener("pointerdown", (event) => {
        if (event.pointerType === "mouse" && event.button !== 0) return;
        clearHoldTimer();
        button.classList.add("is-holding");
        holdTimer = window.setTimeout(() => {
          holdTimer = null;
          button.classList.remove("is-holding");
          if (window.confirm(`Remove ${tag} from this task?`)) {
            void removeTag(tag);
          }
        }, 650);
      });
      ["pointerup", "pointerleave", "pointercancel"].forEach((eventName) => {
        button.addEventListener(eventName, () => {
          clearHoldTimer();
          button.classList.remove("is-holding");
        });
      });
      button.addEventListener("contextmenu", (event) => {
        event.preventDefault();
      });
      button.addEventListener("click", (event) => {
        event.preventDefault();
        event.stopPropagation();
      });
      tagsEl.appendChild(button);
    });

    const addButton = document.createElement("button");
    addButton.className = "task-card-ui__tag task-card-ui__tag--action";
    addButton.type = "button";
    addButton.textContent = "+";
    addButton.title = "Add tag";
    addButton.disabled = busy || !task.taskPath;
    addButton.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      void addTag();
    });
    tagsEl.appendChild(addButton);
  };

  const renderLaneMenu = () => {
    laneMenuEl.innerHTML = "";
    if (!task.taskPath) {
      closeLaneMenu();
      return;
    }
    taskMoveOptions(task.lane).forEach((option) => {
      const button = document.createElement("button");
      button.className = "task-card-ui__lane-menu-item";
      button.type = "button";
      button.textContent = option.label;
      button.disabled = busy;
      button.addEventListener("click", (event) => {
        event.preventDefault();
        event.stopPropagation();
        void moveTask(option.lane);
      });
      laneMenuEl.appendChild(button);
    });
    laneMenuEl.style.display = laneMenuOpen ? "flex" : "none";
  };

  const applyTheme = () => {
    const theme = TASK_LANE_THEMES[task.lane] || TASK_LANE_THEMES.backlog;
    cardEl.style.setProperty("--task-accent", theme.accent);
    cardEl.style.setProperty("--task-accent-soft", theme.accentSoft);
    cardEl.style.setProperty("--task-muted", theme.muted);
    cardEl.style.setProperty("--task-button-ink", theme.buttonInk);
  };

  const render = () => {
    applyTheme();
    laneToggleEl.textContent = "";
    const laneLabelEl = document.createElement("span");
    laneLabelEl.className = "task-card-ui__lane";
    laneLabelEl.textContent = TASK_LANE_LABELS[task.lane] || "Task";
    const caretEl = document.createElement("span");
    caretEl.className = `task-card-ui__lane-caret${laneMenuOpen ? " open" : ""}`;
    caretEl.textContent = "▾";
    laneToggleEl.append(laneLabelEl, caretEl);
    laneToggleEl.disabled = busy || !task.taskPath;
    laneToggleEl.setAttribute("aria-expanded", laneMenuOpen ? "true" : "false");

    setStatus(statusLabel, statusKind);

    if (editingField === "title") {
      renderInlineEditor(titleEl, "title", task.title, "");
    } else {
      titleEl.innerHTML = "";
      const button = document.createElement("button");
      button.className = "task-card-ui__title task-card-ui__text-button";
      button.type = "button";
      button.disabled = busy || !task.taskPath;
      button.textContent = task.title || "(Untitled task)";
      button.addEventListener("click", beginTitleEdit);
      titleEl.appendChild(button);
    }

    const bodySummary = summarizeTaskBody(task);
    if (editingField === "body") {
      renderInlineEditor(bodyEl, "body", task.body, "Add description");
    } else {
      bodyEl.innerHTML = "";
      const button = document.createElement("button");
      button.className = `task-card-ui__body task-card-ui__text-button task-card-ui__body-markdown${
        bodySummary ? "" : " is-placeholder"
      }`;
      button.type = "button";
      button.disabled = busy || !task.taskPath;
      const inner = document.createElement("span");
      inner.className = "task-card-ui__body-markdown-inner";
      inner.innerHTML = bodySummary ? renderTaskBodyMarkdown(host, bodySummary) : "Add description";
      button.appendChild(inner);
      button.addEventListener("click", beginBodyEdit);
      bodyEl.appendChild(button);
    }

    renderTags();
    renderLaneMenu();

    const dueText = formatTaskDue(task);
    dueEl.textContent = dueText;
    metaEl.style.display = dueText ? "flex" : "none";

    publishLiveContent();
    setBusy(busy);
  };

  const handleLaneToggle = (event) => {
    event.preventDefault();
    event.stopPropagation();
    if (laneMenuOpen) closeLaneMenu();
    else openLaneMenu();
    render();
  };

  const handleDocumentPointerDown = (event) => {
    if (!laneMenuOpen) return;
    if (!(event.target instanceof Node)) return;
    if (laneWrapEl.contains(event.target)) return;
    closeLaneMenu();
    render();
  };

  const handleEscape = (event) => {
    if (event.key !== "Escape" || !laneMenuOpen) return;
    closeLaneMenu();
    render();
  };

  laneToggleEl.addEventListener("click", handleLaneToggle);
  document.addEventListener("pointerdown", handleDocumentPointerDown);
  document.addEventListener("keydown", handleEscape);

  render();

  return {
    update({ item: nextItem, state: nextState }) {
      task = normalizeTask(nextState, nextItem.title);
      errorText = "";
      statusLabel = "";
      statusKind = "neutral";
      laneMenuOpen = false;
      editingField = null;
      render();
    },
    destroy() {
      clearHoldTimer();
      document.removeEventListener("pointerdown", handleDocumentPointerDown);
      document.removeEventListener("keydown", handleEscape);
      laneToggleEl.removeEventListener("click", handleLaneToggle);
      host.setRefreshHandler(null);
      host.setLiveContent(null);
      host.clearSelection();
    },
  };
}
