"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isValidRequiredSkillRef = isValidRequiredSkillRef;
exports.dedupeRequiredSkillRefs = dedupeRequiredSkillRefs;
exports.checkRequiredSkillAvailability = checkRequiredSkillAvailability;
exports.formatRequiredSkillAvailabilityReport = formatRequiredSkillAvailabilityReport;
exports.resolveRunnableWorkersForRequiredSkills = resolveRunnableWorkersForRequiredSkills;
exports.collectRequiredSkillsForWorkflowTree = collectRequiredSkillsForWorkflowTree;
const node_path_1 = __importDefault(require("node:path"));
const store_1 = require("../auth-profiles/store");
const requirement_exclusions_1 = require("../catalog/requirement-exclusions");
const catalog_1 = require("../i18n/catalog");
const skill_inventory_1 = require("./skill-inventory");
const validation_1 = require("./validation");
const WORKER_SKILL_DIRECTORY_BY_NAME = {
    codex: ".agents/skills",
    claude: ".claude/skills",
};
function isValidRequiredSkillRef(skillRef) {
    return (0, skill_inventory_1.isValidRequiredSkillRef)(skillRef);
}
function dedupeRequiredSkillRefs(skillRefs) {
    return (0, skill_inventory_1.dedupeSkillRefs)(skillRefs);
}
function resolveWorkerRequiredSkillPath(input) {
    const parsed = (0, skill_inventory_1.parseRequiredSkillRef)(input.skillRef);
    if (parsed !== null && parsed.worker !== null) {
        return `${input.skillRef} (provider-qualified skill ref)`;
    }
    return node_path_1.default.join(input.repoRoot, resolveRequiredSkillDirectory(input.worker), input.skillRef, "SKILL.md");
}
function resolveRequiredSkillDirectory(worker) {
    return worker === "claude"
        ? WORKER_SKILL_DIRECTORY_BY_NAME.claude
        : WORKER_SKILL_DIRECTORY_BY_NAME.codex;
}
async function findMissingRequiredSkills(input) {
    const configuredWorkers = [...input.workers].sort();
    const exclusions = await (0, requirement_exclusions_1.loadEffectiveRequirementExclusions)({
        repoRoot: input.repoRoot,
        ...(input.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.homeDirectory }),
    });
    const { items } = await (0, skill_inventory_1.listAvailableSkills)({
        repoRoot: input.repoRoot,
        workers: configuredWorkers,
        configuredWorkers,
        ...(input.authProfiles === undefined
            ? {}
            : { authProfiles: input.authProfiles }),
        ...(input.env === undefined ? {} : { env: input.env }),
        ...(input.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.homeDirectory }),
        includeExcluded: true,
    });
    const availableRefs = new Set(items
        .filter((item) => item.usableForRequiredSkills)
        .map((item) => `${item.worker}\0${item.ref}`));
    const missing = [];
    for (const skillRef of dedupeRequiredSkillRefs(input.skillRefs).sort()) {
        const targetWorkers = resolveTargetWorkersForRequiredSkillRef({
            skillRef,
            workers: configuredWorkers,
        });
        const satisfyingWorkers = targetWorkers.filter((worker) => hasAvailableRequiredSkillRef({
            availableRefs,
            worker,
            skillRef,
            configuredWorkers,
        }));
        if (satisfyingWorkers.length > 0) {
            continue;
        }
        for (const worker of targetWorkers) {
            const excludedFromRequires = findRequiredSkillInventoryExclusion({
                items,
                skillRef,
                worker,
                configuredWorkers,
            }) ??
                findRequiredSkillExclusion({
                    exclusions,
                    skillRef,
                    worker,
                    configuredWorkers,
                });
            const skillPath = resolveWorkerRequiredSkillPath({
                repoRoot: input.repoRoot,
                worker,
                skillRef,
            });
            missing.push({
                skillRef,
                worker,
                skillPath,
                ...(excludedFromRequires === null ? {} : { excludedFromRequires }),
            });
        }
    }
    return missing;
}
async function checkRequiredSkillAvailability(input) {
    const authProfiles = input.authProfiles ??
        (await (0, store_1.loadAuthProfiles)(input.homeDirectory).then((file) => file.profiles));
    const skillRefs = dedupeRequiredSkillRefs(input.skillRefs).sort();
    const missing = await findMissingRequiredSkills({
        repoRoot: input.repoRoot,
        workers: input.workers,
        authProfiles,
        ...(input.env === undefined ? {} : { env: input.env }),
        ...(input.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.homeDirectory }),
        skillRefs,
    });
    const runnableWorkers = await resolveRunnableWorkersForRequiredSkills({
        repoRoot: input.repoRoot,
        workers: input.workers,
        authProfiles,
        ...(input.env === undefined ? {} : { env: input.env }),
        ...(input.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.homeDirectory }),
        skillRefs,
    });
    return {
        ok: missing.length === 0 && runnableWorkers.length > 0,
        skillRefs,
        missing,
        runnableWorkers,
    };
}
function formatRequiredSkillAvailabilityReport(input) {
    const { report, locale } = input;
    if (report.ok) {
        return report.skillRefs.length === 0
            ? (0, catalog_1.translate)(locale, "workflow.requiredSkills.report.none")
            : (0, catalog_1.translate)(locale, "workflow.requiredSkills.report.available", {
                skillRefs: report.skillRefs.join(", "),
            });
    }
    return [
        (0, catalog_1.translate)(locale, "workflow.requiredSkills.report.missing"),
        ...(report.runnableWorkers.length === 0
            ? [
                (0, catalog_1.translate)(locale, "workflow.requiredSkills.report.noWorker", {
                    skillRefs: report.skillRefs.join(", "),
                }),
            ]
            : []),
        ...report.missing.map((missing) => missing.excludedFromRequires === undefined
            ? (0, catalog_1.translate)(locale, "workflow.requiredSkills.report.item", {
                skillRef: missing.skillRef,
                worker: missing.worker,
                skillPath: missing.skillPath,
            })
            : (0, catalog_1.translate)(locale, "workflow.requiredSkills.report.itemExcluded", {
                skillRef: missing.skillRef,
                worker: missing.worker,
                reason: missing.excludedFromRequires.reason,
                path: missing.excludedFromRequires.path,
            })),
    ].join("\n");
}
async function resolveRunnableWorkersForRequiredSkills(input) {
    const skillRefs = dedupeRequiredSkillRefs(input.skillRefs);
    const configuredWorkers = [...input.workers].sort();
    if (skillRefs.length === 0) {
        return configuredWorkers;
    }
    const { items } = await (0, skill_inventory_1.listAvailableSkills)({
        repoRoot: input.repoRoot,
        workers: configuredWorkers,
        configuredWorkers,
        ...(input.authProfiles === undefined
            ? {}
            : { authProfiles: input.authProfiles }),
        ...(input.env === undefined ? {} : { env: input.env }),
        ...(input.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.homeDirectory }),
        includeExcluded: true,
    });
    const availableRefs = new Set(items
        .filter((item) => item.usableForRequiredSkills)
        .map((item) => `${item.worker}\0${item.ref}`));
    return configuredWorkers.filter((worker) => skillRefs.every((skillRef) => (0, skill_inventory_1.requiredSkillRefCanRunOnWorker)(skillRef, worker) &&
        hasAvailableRequiredSkillRef({
            availableRefs,
            worker,
            skillRef,
            configuredWorkers,
        })));
}
function findRequiredSkillInventoryExclusion(input) {
    const parsed = (0, skill_inventory_1.parseRequiredSkillRef)(input.skillRef, input.configuredWorkers);
    if (parsed === null) {
        return null;
    }
    if (parsed.worker !== null && parsed.worker !== input.worker) {
        return null;
    }
    const item = input.items.find((candidate) => candidate.worker === input.worker &&
        (candidate.ref === input.skillRef || candidate.ref === parsed.skillRef));
    return item?.excludedFromRequires ?? null;
}
function findRequiredSkillExclusion(input) {
    const parsed = (0, skill_inventory_1.parseRequiredSkillRef)(input.skillRef, input.configuredWorkers);
    if (parsed === null) {
        return null;
    }
    return ((0, requirement_exclusions_1.findRequirementExclusionMatch)(input.exclusions, "skill", {
        ref: input.skillRef,
        id: parsed.skillRef,
    }) ??
        (0, requirement_exclusions_1.findRequirementExclusionMatch)(input.exclusions, "skill", {
            ref: `${input.worker}:${parsed.skillRef}`,
            id: parsed.skillRef,
        }));
}
function hasAvailableRequiredSkillRef(input) {
    const parsed = (0, skill_inventory_1.parseRequiredSkillRef)(input.skillRef, input.configuredWorkers);
    if (parsed === null) {
        return false;
    }
    if (parsed.worker !== null && parsed.worker !== input.worker) {
        return false;
    }
    return (input.availableRefs.has(`${input.worker}\0${input.skillRef}`) ||
        input.availableRefs.has(`${input.worker}\0${parsed.skillRef}`));
}
function resolveTargetWorkersForRequiredSkillRef(input) {
    const parsed = (0, skill_inventory_1.parseRequiredSkillRef)(input.skillRef, input.workers);
    if (parsed === null) {
        return [];
    }
    if (parsed.worker === null) {
        return [...input.workers];
    }
    return [parsed.worker];
}
function collectRequiredSkillsForWorkflowTree(input) {
    const skillRefs = new Set();
    const visitedTemplatePaths = new Set();
    const visit = (template, activeStack, depth) => {
        if (visitedTemplatePaths.has(template.templatePath)) {
            return;
        }
        visitedTemplatePaths.add(template.templatePath);
        for (const skillRef of template.metadata?.requiredSkills ?? []) {
            skillRefs.add(skillRef);
        }
        if (depth >= input.maxNestedWorkflowDepth) {
            return;
        }
        const nextActiveStack = [...activeStack, template.templatePath];
        for (const { templateName: templatePath } of (0, validation_1.scanWorkflowTemplateDirectives)(template).directives) {
            if (nextActiveStack.includes(templatePath)) {
                continue;
            }
            const childTemplate = input.catalog.templateByPath.get(templatePath);
            if (childTemplate === undefined) {
                continue;
            }
            visit(childTemplate, nextActiveStack, depth + 1);
        }
    };
    visit(input.rootTemplate, [], 0);
    return [...skillRefs].sort();
}
