import { TaskNotFoundError } from "@poe-code/task-list";
import { S } from "toolcraft-schema";
import { hasOwnErrorCode } from "../error-codes.js";
import { UserError, defineCommand, defineGroup } from "../index.js";
import { ensureApprovalList } from "./approval-tasks.js";
import { runApproval } from "./runner.js";
const approvalsGroupSymbol = Symbol("toolcraft.humanInLoop.approvalsBuiltIn");
const listScope = ["cli", "mcp", "sdk"];
const runScope = ["cli"];
const listParams = S.Object({
    state: S.Optional(S.String())
});
const showParams = S.Object({
    approvalId: S.String()
});
const runParams = S.Object({
    approvalId: S.String(),
    dryRun: S.Optional(S.Boolean({
        description: "Preview the approval without prompting or executing it",
        scope: ["cli"]
    }))
});
export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
    name: "approvals",
    description: "Inspect and execute queued approvals.",
    children: [
        defineCommand({
            name: "list",
            description: "List queued approvals.",
            scope: listScope,
            params: listParams,
            handler: async ({ params, runtimeOptions }) => {
                try {
                    const { tasks } = await ensureApprovalList(runtimeOptions, { create: false });
                    return loadApprovals(tasks, params.state);
                }
                catch (error) {
                    if (isMissingStateError(error)) {
                        return [];
                    }
                    throw error;
                }
            },
            render: {
                rich: (result, primitives) => renderApprovalList(result, primitives),
                markdown: (result) => renderApprovalListMarkdown(result),
                json: (result) => result
            }
        }),
        defineCommand({
            name: "show",
            description: "Show one approval.",
            scope: listScope,
            params: showParams,
            handler: async ({ params, runtimeOptions }) => {
                try {
                    const { tasks } = await ensureApprovalList(runtimeOptions, { create: false });
                    return tasks.get(params.approvalId);
                }
                catch (error) {
                    if (isMissingStateError(error)) {
                        throw new TaskNotFoundError(`Task "approvals/${params.approvalId}" not found.`);
                    }
                    throw error;
                }
            },
            render: {
                rich: (result, primitives) => renderApprovalDetails(result, primitives),
                markdown: (result) => renderApprovalDetailsMarkdown(result),
                json: (result) => result
            }
        }),
        defineCommand({
            name: "run",
            description: "Run one queued approval.",
            scope: runScope,
            params: runParams,
            handler: async ({ params, runtimeOptions, root }) => {
                if (params.dryRun === true) {
                    const { tasks } = await ensureApprovalList(runtimeOptions, { create: false });
                    return tasks.get(params.approvalId);
                }
                return runApproval(params.approvalId, runtimeOptions, root);
            },
            render: {
                rich: (result, primitives) => {
                    if (result)
                        renderApprovalDetails(result, primitives);
                },
                markdown: (result) => result ? renderApprovalDetailsMarkdown(result) : "",
                json: (result) => result
            }
        })
    ]
}));
export function mergeApprovalsGroup(root) {
    const existing = root.children.find((child) => child.name === approvalsGroup.name);
    if (existing !== undefined) {
        if (isApprovalsBuiltIn(existing)) {
            return root;
        }
        throw new UserError("'approvals' is reserved for human-in-loop built-ins");
    }
    return {
        ...root,
        children: [...root.children, approvalsGroup]
    };
}
function markApprovalsBuiltIn(group) {
    Object.defineProperty(group, approvalsGroupSymbol, {
        configurable: false,
        enumerable: false,
        value: true,
        writable: false
    });
    return group;
}
function isApprovalsBuiltIn(node) {
    return (node.kind === "group" &&
        node[approvalsGroupSymbol] === true);
}
async function loadApprovals(tasks, stateFilter) {
    const states = splitStateFilter(stateFilter);
    if (states.length === 0) {
        return tasks.all();
    }
    const seenIds = new Set();
    const approvals = [];
    for (const state of states) {
        const matching = await tasks.all({
            state
        });
        for (const task of matching) {
            if (seenIds.has(task.qualifiedId)) {
                continue;
            }
            seenIds.add(task.qualifiedId);
            approvals.push(task);
        }
    }
    return approvals;
}
function splitStateFilter(stateFilter) {
    if (stateFilter === undefined) {
        return [];
    }
    const seen = new Set();
    const states = [];
    for (const value of stateFilter.split(",")) {
        const trimmed = value.trim();
        if (trimmed.length === 0 || seen.has(trimmed)) {
            continue;
        }
        seen.add(trimmed);
        states.push(trimmed);
    }
    return states;
}
function renderApprovalList(result, { logger, renderTable, getTheme }) {
    if (result.length === 0) {
        logger.message("No approvals found.");
        return;
    }
    logger.message(renderTable({
        theme: getTheme(),
        columns: [
            { name: "id", title: "ID", alignment: "left", maxLen: 24 },
            { name: "state", title: "State", alignment: "left", maxLen: 18 },
            { name: "name", title: "Name", alignment: "left", maxLen: 60 }
        ],
        rows: result.map((task) => ({
            id: task.id,
            state: task.state,
            name: task.name
        }))
    }));
}
function renderApprovalListMarkdown(result) {
    if (result.length === 0) {
        return "No approvals found.";
    }
    const lines = ["| ID | State | Name |", "| :--- | :--- | :--- |"];
    for (const task of result) {
        lines.push(`| ${escapeMarkdownCell(task.id)} | ${escapeMarkdownCell(task.state)} | ${escapeMarkdownCell(task.name)} |`);
    }
    return lines.join("\n");
}
function renderApprovalDetails(result, { logger, renderTable, getTheme }) {
    logger.message(renderTable({
        theme: getTheme(),
        columns: [
            { name: "key", title: "Key", alignment: "left", maxLen: 18 },
            { name: "value", title: "Value", alignment: "left", maxLen: 80 }
        ],
        rows: Object.entries(taskToRecord(result)).map(([key, value]) => ({
            key,
            value: stringifyValue(value)
        }))
    }));
}
function renderApprovalDetailsMarkdown(result) {
    return Object.entries(taskToRecord(result))
        .map(([key, value]) => `- ${key}: ${stringifyValue(value)}`)
        .join("\n");
}
function taskToRecord(task) {
    return {
        list: task.list,
        id: task.id,
        qualifiedId: task.qualifiedId,
        name: task.name,
        state: task.state,
        description: task.description,
        metadata: task.metadata
    };
}
function stringifyValue(value) {
    if (value === undefined) {
        return "";
    }
    if (typeof value === "string") {
        return value;
    }
    try {
        return JSON.stringify(value) ?? String(value);
    }
    catch {
        return String(value);
    }
}
function escapeMarkdownCell(value) {
    return value.replaceAll("|", "\\|");
}
function isMissingStateError(error) {
    return hasOwnErrorCode(error, "ENOENT");
}
