import crypto from "node:crypto";
import { existsSync } from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import { sortTodos } from "./todos-model.js";
import { getTodoPath } from "./todos-paths.js";
function parseFrontMatter(text, idFallback) {
    const data = {
        id: idFallback,
        title: "",
        tags: [],
        status: "open",
        created_at: "",
        assigned_to_session: undefined,
    };
    const trimmed = text.trim();
    if (!trimmed)
        return data;
    try {
        const parsed = JSON.parse(trimmed);
        if (!parsed || typeof parsed !== "object")
            return data;
        if (typeof parsed.id === "string" && parsed.id)
            data.id = parsed.id;
        if (typeof parsed.title === "string")
            data.title = parsed.title;
        if (typeof parsed.status === "string" && parsed.status)
            data.status = parsed.status;
        if (typeof parsed.created_at === "string")
            data.created_at = parsed.created_at;
        if (typeof parsed.assigned_to_session === "string" &&
            parsed.assigned_to_session.trim()) {
            data.assigned_to_session = parsed.assigned_to_session;
        }
        if (Array.isArray(parsed.tags)) {
            data.tags = parsed.tags.filter((tag) => typeof tag === "string");
        }
    }
    catch {
        return data;
    }
    return data;
}
function findJsonObjectEnd(content) {
    let depth = 0;
    let inString = false;
    let escaped = false;
    for (let i = 0; i < content.length; i += 1) {
        const char = content[i];
        if (inString) {
            if (escaped) {
                escaped = false;
                continue;
            }
            if (char === "\\") {
                escaped = true;
                continue;
            }
            if (char === '"') {
                inString = false;
            }
            continue;
        }
        if (char === '"') {
            inString = true;
            continue;
        }
        if (char === "{") {
            depth += 1;
            continue;
        }
        if (char === "}") {
            depth -= 1;
            if (depth === 0)
                return i;
        }
    }
    return -1;
}
function splitFrontMatter(content) {
    if (!content.startsWith("{")) {
        return { frontMatter: "", body: content };
    }
    const endIndex = findJsonObjectEnd(content);
    if (endIndex === -1) {
        return { frontMatter: "", body: content };
    }
    const frontMatter = content.slice(0, endIndex + 1);
    const body = content.slice(endIndex + 1).replace(/^\r?\n+/, "");
    return { frontMatter, body };
}
function parseTodoContent(content, idFallback) {
    const { frontMatter, body } = splitFrontMatter(content);
    const parsed = parseFrontMatter(frontMatter, idFallback);
    return {
        id: idFallback,
        title: parsed.title,
        tags: parsed.tags ?? [],
        status: parsed.status,
        created_at: parsed.created_at,
        assigned_to_session: parsed.assigned_to_session,
        body: body ?? "",
    };
}
function serializeTodo(todo) {
    const frontMatter = JSON.stringify({
        id: todo.id,
        title: todo.title,
        tags: todo.tags ?? [],
        status: todo.status,
        created_at: todo.created_at,
        assigned_to_session: todo.assigned_to_session || undefined,
    }, null, 2);
    const body = todo.body ?? "";
    const trimmedBody = body.replace(/^\n+/, "").replace(/\s+$/, "");
    if (!trimmedBody)
        return `${frontMatter}\n`;
    return `${frontMatter}\n\n${trimmedBody}\n`;
}
export async function ensureTodosDir(todosDir) {
    await fs.mkdir(todosDir, { recursive: true });
}
export async function readTodoFile(filePath, idFallback) {
    const content = await fs.readFile(filePath, "utf8");
    return parseTodoContent(content, idFallback);
}
export async function writeTodoFile(filePath, todo) {
    await fs.writeFile(filePath, serializeTodo(todo), "utf8");
}
export async function generateTodoId(todosDir) {
    for (let attempt = 0; attempt < 10; attempt += 1) {
        const id = crypto.randomBytes(4).toString("hex");
        const todoPath = getTodoPath(todosDir, id);
        if (!existsSync(todoPath))
            return id;
    }
    throw new Error("Failed to generate unique todo id");
}
export async function listTodos(todosDir) {
    let entries = [];
    try {
        entries = await fs.readdir(todosDir);
    }
    catch {
        return [];
    }
    const todos = [];
    for (const entry of entries) {
        if (!entry.endsWith(".md"))
            continue;
        const id = entry.slice(0, -3);
        const filePath = path.join(todosDir, entry);
        try {
            const content = await fs.readFile(filePath, "utf8");
            const { frontMatter } = splitFrontMatter(content);
            const parsed = parseFrontMatter(frontMatter, id);
            todos.push({
                id,
                title: parsed.title,
                tags: parsed.tags ?? [],
                status: parsed.status,
                created_at: parsed.created_at,
                assigned_to_session: parsed.assigned_to_session,
            });
        }
        catch {
            // ignore unreadable todo
        }
    }
    return sortTodos(todos);
}
export async function ensureTodoExists(filePath, id) {
    if (!existsSync(filePath))
        return null;
    return readTodoFile(filePath, id);
}
export async function appendTodoBody(filePath, todo, text) {
    const spacer = todo.body.trim().length ? "\n\n" : "";
    todo.body = `${todo.body.replace(/\s+$/, "")}${spacer}${text.trim()}\n`;
    await writeTodoFile(filePath, todo);
    return todo;
}
//# sourceMappingURL=todos-storage.js.map