import path from "node:path";
import * as fsPromises from "node:fs/promises";
import { discoverPlans } from "@poe-code/agent-harness-tools";
import { parsePlan } from "./parser.js";
import { isNotFound } from "../utils.js";
function createDefaultFs() {
    return {
        readFile: fsPromises.readFile,
        readdir: fsPromises.readdir,
        stat: async (filePath) => {
            const stat = await fsPromises.stat(filePath);
            return {
                isFile: () => stat.isFile(),
                isDirectory: () => stat.isDirectory(),
                mtimeMs: stat.mtimeMs
            };
        }
    };
}
function countCompletedTasks(planPath, content) {
    const plan = parsePlan(content);
    const total = plan.tasks.length;
    const done = plan.tasks.filter((task) => {
        if (typeof task.status === "string") {
            return task.status === "done";
        }
        const statuses = Object.values(task.status);
        return statuses.length > 0 && statuses.every((status) => status === "done");
    }).length;
    return {
        path: planPath,
        done,
        total
    };
}
async function ensurePlanExists(fs, cwd, planPath) {
    const absolutePath = path.isAbsolute(planPath) ? planPath : path.resolve(cwd, planPath);
    try {
        const stat = await fs.stat(absolutePath);
        if (!stat.isFile()) {
            throw new Error(`Plan not found at "${planPath}".`);
        }
    }
    catch (error) {
        if (isNotFound(error)) {
            throw new Error(`Plan not found at "${planPath}".`);
        }
        throw error;
    }
}
async function listPlanCandidates(fs, cwd, homeDir, planDirectory) {
    const dir = planDirectory?.trim() || "docs/plans";
    const plans = await discoverPlans({
        cwd,
        homeDir,
        planDirectory: dir,
        kinds: ["pipeline"],
        fs: fs
    });
    const candidates = await Promise.all(plans.map(async (plan) => {
        const content = await fs.readFile(plan.absolutePath, "utf8");
        return countCompletedTasks(plan.displayPath, content);
    }));
    candidates.sort((left, right) => left.path.localeCompare(right.path));
    return candidates;
}
function resolveAbsoluteDirectory(dir, cwd, homeDir) {
    if (dir.startsWith("~/")) {
        return path.join(homeDir, dir.slice(2));
    }
    return path.isAbsolute(dir) ? dir : path.resolve(cwd, dir);
}
function describeDiscoveryDirectory(planDirectory) {
    const configured = planDirectory?.trim();
    return configured && configured.length > 0 ? configured : "docs/plans";
}
export function resolvePlanDirectory(options) {
    const dir = options.planDirectory?.trim() || "docs/plans";
    return resolveAbsoluteDirectory(dir, options.cwd, options.homeDir);
}
export function resolveAbsolutePlanPath(planPath, cwd, homeDir) {
    if (planPath.startsWith("~/")) {
        return path.join(homeDir, planPath.slice(2));
    }
    return path.isAbsolute(planPath) ? planPath : path.resolve(cwd, planPath);
}
export async function resolvePlanPath(options) {
    const fs = options.fs ?? createDefaultFs();
    const planPaths = await resolvePlanPaths({
        ...options,
        fs
    });
    return planPaths?.[0] ?? null;
}
export async function resolvePlanPaths(options) {
    const fs = options.fs ?? createDefaultFs();
    const explicitPlans = [...(options.plan ? [options.plan] : []), ...(options.plans ?? [])]
        .map((planPath) => planPath.trim())
        .filter((planPath) => planPath.length > 0);
    if (explicitPlans.length > 0) {
        for (const planPath of explicitPlans) {
            await ensurePlanExists(fs, options.cwd, planPath);
        }
        return explicitPlans;
    }
    const candidates = await listPlanCandidates(fs, options.cwd, options.homeDir, options.planDirectory);
    if (candidates.length >= 1) {
        if (options.assumeYes) {
            return [candidates[0].path];
        }
        if (options.selectPlans) {
            return options.selectPlans({
                message: "Select pipeline plans to run",
                options: candidates.map((candidate) => ({
                    label: `${candidate.path} (${candidate.done}/${candidate.total})`,
                    value: candidate.path
                })),
                required: true
            });
        }
        if (!options.selectPlan) {
            return null;
        }
        const selectedPlan = await options.selectPlan({
            message: "Select a pipeline plan to run",
            options: candidates.map((candidate) => ({
                label: `${candidate.path} (${candidate.done}/${candidate.total})`,
                value: candidate.path
            }))
        });
        return selectedPlan ? [selectedPlan] : null;
    }
    if (options.assumeYes) {
        const directory = describeDiscoveryDirectory(options.planDirectory);
        throw new Error(`No plan found under ${directory}. Provide --plan <path> to an existing plan file.`);
    }
    if (!options.promptForPath) {
        return null;
    }
    const selectedPath = await options.promptForPath({
        message: "Enter the pipeline plan path",
        placeholder: path.join(describeDiscoveryDirectory(options.planDirectory), "plan.md")
    });
    return selectedPath ? [selectedPath] : null;
}
