#!/usr/bin/env node

import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const taskmasterPath = path.join(repoRoot, ".taskmaster", "tasks", "tasks.json");
const outputDir = path.join(repoRoot, "Taskmaster");
const taskDir = path.join(outputDir, "Tasks");

const STATUS_CHECKBOX = {
  done: "x",
  completed: "x",
};

const PRIORITY_TAG = {
  high: "#priority/high",
  medium: "#priority/medium",
  low: "#priority/low",
};

const PRIORITY_EMOJI = {
  highest: "🔺",
  high: "⏫",
  medium: "🔼",
  low: "🔽",
  lowest: "⏬",
};

const source = JSON.parse(await readFile(taskmasterPath, "utf8"));
const tasks = source.master?.tasks ?? source.tasks ?? [];
const generatedAt = new Date().toISOString();

await rm(outputDir, { recursive: true, force: true });
await mkdir(taskDir, { recursive: true });

const flatTasks = tasks.flatMap((task) => {
  const parent = normalizeTask(task);
  const subtasks = (task.subtasks ?? []).map((subtask) =>
    normalizeTask({
      ...subtask,
      id: `${task.id}.${subtask.id}`,
      parentId: task.id,
      parentTitle: task.title,
      priority: subtask.priority ?? task.priority,
    }),
  );
  return [parent, ...subtasks];
});

for (const task of flatTasks) {
  await writeFile(path.join(taskDir, `${slugTaskId(task.id)}.md`), renderTaskNote(task));
}

await writeFile(path.join(outputDir, "README.md"), renderDashboard(flatTasks));
await writeFile(path.join(outputDir, "tasks.md"), renderTasksFile(flatTasks));

console.log(`Exported ${flatTasks.length} Taskmaster tasks to ${path.relative(repoRoot, outputDir)}`);

function normalizeTask(task) {
  return {
    id: String(task.id),
    title: task.title ?? "Untitled task",
    description: task.description ?? "",
    details: task.details ?? "",
    testStrategy: task.testStrategy ?? "",
    status: task.status ?? "pending",
    priority: task.priority ?? "medium",
    dependencies: (task.dependencies ?? []).map(String),
    parentId: task.parentId && task.parentId !== "undefined" ? String(task.parentId) : "",
    parentTitle: task.parentTitle ?? "",
  };
}

function renderTaskNote(task) {
  return `---
taskmaster_id: "${yamlEscape(task.id)}"
status: "${yamlEscape(task.status)}"
priority: "${yamlEscape(task.priority)}"
depends_on: [${task.dependencies.map((id) => `"${yamlEscape(id)}"`).join(", ")}]
parent_id: "${yamlEscape(task.parentId)}"
source: "taskmaster"
generated_at: "${generatedAt}"
---

# ${task.id} - ${task.title}

${renderObsidianTaskLine(task)}

## Metadata

| Field | Value |
| --- | --- |
| Taskmaster ID | ${task.id} |
| Status | ${task.status} |
| Priority | ${task.priority} |
| Dependencies | ${task.dependencies.length ? task.dependencies.join(", ") : "None"} |
| Parent | ${task.parentId ? `${task.parentId} - ${task.parentTitle}` : "None"} |

## Description

${task.description || "_No description._"}

## Details

${task.details || "_No details._"}

## Verification

${task.testStrategy || "_No verification strategy._"}
`;
}

function renderDashboard(tasks) {
  const rows = tasks
    .map((task) => `| [[Tasks/${slugTaskId(task.id)}|${task.id}]] | ${escapeTable(task.title)} | ${task.status} | ${task.priority} | ${task.dependencies.length ? task.dependencies.join(", ") : "None"} |`)
    .join("\n");

  return `# Taskmaster Dashboard

Generated from \`.taskmaster/tasks/tasks.json\` at ${generatedAt}.

Taskmaster remains the canonical source of truth. Re-run:

\`\`\`sh
node scripts/export-taskmaster-to-obsidian.mjs
\`\`\`

## Status Summary

${renderStatusSummary(tasks)}

## Task Index

| ID | Title | Status | Priority | Dependencies |
| --- | --- | --- | --- | --- |
${rows}

## Obsidian Tasks Query

\`\`\`tasks
not done
tag includes #taskmaster
sort by priority
sort by description
\`\`\`
`;
}

function renderTasksFile(tasks) {
  const lines = tasks.map(renderObsidianTaskLine).join("\n");

  return `# Taskmaster Tasks

Generated from \`.taskmaster/tasks/tasks.json\` at ${generatedAt}.

These lines use the Obsidian Tasks emoji format:

- standard Markdown checkbox syntax
- \`#taskmaster\` tag for filtering
- priority emoji where available
- \`🆔\` task IDs
- \`⛔\` dependency IDs

${lines}
`;
}

function renderObsidianTaskLine(task) {
  const checkbox = STATUS_CHECKBOX[task.status] ?? " ";
  const priorityTag = PRIORITY_TAG[task.priority] ?? "#priority/none";
  const priorityEmoji = PRIORITY_EMOJI[task.priority] ?? "";
  const dependencyMarkers = task.dependencies.map((id) => `⛔ tm-${safeId(id)}`).join(" ");
  const fields = [
    "#taskmaster",
    priorityTag,
    `#status/${tagValue(task.status)}`,
    priorityEmoji,
    `🆔 tm-${safeId(task.id)}`,
    dependencyMarkers,
  ].filter(Boolean);

  return `- [${checkbox}] ${task.id} - ${task.title} ${fields.join(" ")}`;
}

function renderStatusSummary(tasks) {
  const counts = tasks.reduce((acc, task) => {
    acc[task.status] = (acc[task.status] ?? 0) + 1;
    return acc;
  }, {});

  return Object.entries(counts)
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([status, count]) => `- ${status}: ${count}`)
    .join("\n");
}

function slugTaskId(id) {
  return `task-${safeId(id)}`;
}

function safeId(id) {
  return String(id).replace(/[^A-Za-z0-9_-]+/g, "-");
}

function tagValue(value) {
  return String(value || "none").toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
}

function yamlEscape(value) {
  return String(value ?? "").replace(/\\/g, "\\\\").replace(/"/g, '\\"');
}

function escapeTable(value) {
  return String(value ?? "").replace(/\|/g, "\\|").replace(/\n/g, " ");
}
