/**
 * validate-tasks command — standalone validation of task files.
 *
 * Validates .ailf/tasks/*.yaml and .ailf/tasks/*.task.ts files against the
 * CanonicalTaskSchema without running the full pipeline. Useful for
 * pre-commit hooks and CI checks in external repos.
 *
 * Usage:
 *   ailf validate-tasks .ailf/tasks/
 *   ailf validate-tasks /path/to/external-repo/.ailf/tasks/
 *
 * @see packages/eval/src/adapters/task-sources/repo-schemas.ts
 * @see packages/eval/src/adapters/task-sources/repo-validation.ts
 * @see packages/eval/src/adapters/task-sources/task-file-loader.ts
 */
import { existsSync, readdirSync, readFileSync } from "fs";
import { resolve, relative, basename } from "path";
import { Command } from "commander";
import { load } from "js-yaml";
import { detectLegacyFieldNames, migratePromptShape, parseCanonicalTaskFile, } from "../adapters/task-sources/repo-schemas.js";
import { validateCanonicalTasks, formatRepoValidationResult, } from "../adapters/task-sources/repo-validation.js";
import { discoverTsTaskFiles, loadTsTaskFile, } from "../adapters/task-sources/task-file-loader.js";
export function createValidateTasksCommand() {
    return new Command("tasks")
        .description("Validate task files (YAML and TypeScript) in .ailf/tasks/ against the canonical schema")
        .argument("[path]", "Path to tasks directory (default: .ailf/tasks/)", ".ailf/tasks")
        .option("--strict", "Treat warnings as errors", false)
        .action(async (tasksPath, opts) => {
        const exitCode = await runValidateTasks(tasksPath, opts);
        process.exit(exitCode);
    });
}
/**
 * Execute the validate-tasks command logic. Returns the exit code (0 success,
 * 1 failure) so callers can decide how to surface it — the CLI wrapper calls
 * `process.exit`, tests can assert directly.
 */
export async function runValidateTasks(tasksPath, opts) {
    // Resolve relative to the caller's working directory, not the
    // eval package root (which differs when run via bin/ailf.js)
    const callerCwd = opts.callerCwd ?? process.env.AILF_CALLER_CWD ?? process.cwd();
    const resolvedPath = resolve(callerCwd, tasksPath);
    if (!existsSync(resolvedPath)) {
        console.error(`Directory not found: ${resolvedPath}`);
        return 1;
    }
    const yamlFiles = readdirSync(resolvedPath).filter((f) => (f.endsWith(".yaml") || f.endsWith(".yml")) && !f.startsWith("."));
    const tsFiles = discoverTsTaskFiles(resolvedPath);
    const fileCount = yamlFiles.length + tsFiles.length;
    if (fileCount === 0) {
        console.error(`No task files found in ${resolvedPath}\n` +
            "  Expected .yaml, .yml, .task.ts, or .task.js files");
        return 1;
    }
    console.log(`\nValidating ${fileCount} task file(s) in ${relative(process.cwd(), resolvedPath)}/\n`);
    let totalTasks = 0;
    let hasErrors = false;
    const allTasks = [];
    for (const file of yamlFiles) {
        const filePath = resolve(resolvedPath, file);
        const raw = readFileSync(filePath, "utf-8");
        let parsed;
        try {
            parsed = load(raw);
        }
        catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            console.error(`  ${file}: YAML parse error`);
            console.error(`     ${msg}\n`);
            hasErrors = true;
            continue;
        }
        if (!Array.isArray(parsed)) {
            console.error(`  ${file}: Expected a YAML array of task definitions`);
            hasErrors = true;
            continue;
        }
        if (!validateTaskArray(parsed, file, allTasks)) {
            hasErrors = true;
            continue;
        }
        totalTasks += parsed.length;
    }
    for (const tsFilePath of tsFiles) {
        const file = basename(tsFilePath);
        let loaded;
        try {
            loaded = await loadTsTaskFile(tsFilePath);
        }
        catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            console.error(`  ${file}: Failed to load TypeScript task file`);
            console.error(`     ${msg}\n`);
            hasErrors = true;
            continue;
        }
        if (!validateTaskArray(loaded.tasks, file, allTasks)) {
            hasErrors = true;
            continue;
        }
        totalTasks += loaded.tasks.length;
    }
    if (allTasks.length > 0) {
        console.log();
        const semanticResult = validateCanonicalTasks(allTasks);
        const formatted = formatRepoValidationResult(semanticResult);
        console.log(formatted);
        if (!semanticResult.valid) {
            hasErrors = true;
        }
        if (opts.strict && semanticResult.warnings.length > 0) {
            hasErrors = true;
            console.log("\n  --strict mode: warnings treated as errors");
        }
    }
    console.log(`\n${hasErrors ? "FAIL" : "OK"} ${totalTasks} task${totalTasks === 1 ? "" : "s"} across ${fileCount} file${fileCount === 1 ? "" : "s"}\n`);
    return hasErrors ? 1 : 0;
}
/**
 * Validate an array of raw task entries — runs the legacy-field detector and
 * the canonical Zod schema, appending valid tasks to `accumulator`.
 *
 * Returns `true` when the file is fully valid, `false` when any error was
 * reported (the caller is responsible for flipping its own error flag).
 */
function validateTaskArray(entries, file, accumulator) {
    const legacyWarnings = detectLegacyFieldNames(entries, file);
    if (legacyWarnings.length > 0) {
        console.error(`  ${file}: Uses legacy field names`);
        for (const w of legacyWarnings) {
            console.error(`     ${w}`);
        }
        console.error();
        return false;
    }
    // W0193: pre-migrate legacy prompt.vars.{task,docs,__featureArea} shape
    // and surface deprecation warnings (non-fatal — the file still validates).
    const { migrated, warnings: deprecationWarnings } = migratePromptShape(entries, file);
    for (const warning of deprecationWarnings) {
        console.warn(`  ${warning}`);
    }
    try {
        const tasks = parseCanonicalTaskFile(migrated, file);
        console.log(`  ${file}: ${tasks.length} task${tasks.length === 1 ? "" : "s"} valid`);
        accumulator.push(...tasks);
        return true;
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        console.error(`  ${file}: Schema validation failed`);
        console.error(`${msg
            .split("\n")
            .map((l) => `     ${l}`)
            .join("\n")}\n`);
        return false;
    }
}
