"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CAPABILITY_SCHEMA_VERSION = exports.CAPABILITY_SEARCH_KIND = exports.CAPABILITY_LIST_KIND = void 0;
exports.runCapabilityCommand = runCapabilityCommand;
const store_1 = require("../auth-profiles/store");
const control_plane_config_1 = require("../config/control-plane-config");
const fs_errors_1 = require("../core/fs-errors");
const search_text_1 = require("../core/search-text");
const requirement_exclusions_1 = require("../catalog/requirement-exclusions");
const inventory_1 = require("../mcp/inventory");
const search_1 = require("../mcp/search");
const search_2 = require("../skills/search");
const skill_inventory_1 = require("../workflows/skill-inventory");
const json_output_1 = require("./json-output");
exports.CAPABILITY_LIST_KIND = "alpaca.capabilityList";
exports.CAPABILITY_SEARCH_KIND = "alpaca.capabilitySearch";
exports.CAPABILITY_SCHEMA_VERSION = 2;
async function runCapabilityCommand(cwd, args, deps = {}) {
    switch (args.action) {
        case "list":
            return runCapabilityListCommand(cwd, args, deps);
        case "search":
            return runCapabilitySearchCommand(cwd, args, deps);
    }
}
async function runCapabilityListCommand(cwd, args, deps) {
    const context = await loadCapabilityCommandContext(cwd, deps.homeDirectory);
    const workers = resolveCapabilityTargets(args.target, context.configuredWorkers);
    const skillResult = await (0, skill_inventory_1.listAvailableSkills)({
        repoRoot: cwd,
        workers,
        configuredWorkers: context.configuredWorkers,
        authProfiles: context.authProfiles,
        includeExcluded: args.includeExcluded === true,
        ...(deps.homeDirectory === undefined
            ? {}
            : { homeDirectory: deps.homeDirectory }),
    });
    const mcpResult = await (0, inventory_1.listAvailableMcpServers)({
        repoRoot: cwd,
        workers,
        configuredWorkers: context.configuredWorkers,
        authProfiles: context.authProfiles,
        includeExcluded: args.includeExcluded === true,
        ...(deps.homeDirectory === undefined
            ? {}
            : { homeDirectory: deps.homeDirectory }),
    });
    const result = {
        kind: exports.CAPABILITY_LIST_KIND,
        schemaVersion: exports.CAPABILITY_SCHEMA_VERSION,
        repositoryRoot: cwd,
        workers,
        items: sortCapabilityListItems([
            ...skillResult.items.map((item) => ({ type: "skill", ...item })),
            ...mcpResult.items.map((item) => ({ type: "mcp", ...item })),
        ]),
        diagnostics: mcpResult.diagnostics,
        ...mergeRequirementExclusionHints([
            skillResult.exclusionHint,
            mcpResult.exclusionHint,
        ]),
    };
    (0, json_output_1.printCommandJson)(result);
    return 0;
}
async function runCapabilitySearchCommand(cwd, args, deps) {
    const context = await loadCapabilityCommandContext(cwd, deps.homeDirectory);
    const targets = resolveCapabilityTargets(args.target, context.configuredWorkers);
    const [skillResult, mcpResult] = await Promise.all([
        (0, search_2.searchSkills)({
            repoRoot: cwd,
            query: args.query,
            targets,
            mode: args.mode,
            configuredWorkers: context.configuredWorkers,
            authProfiles: context.authProfiles,
            includeExcluded: args.includeExcluded === true,
            ...(deps.homeDirectory === undefined
                ? {}
                : { homeDirectory: deps.homeDirectory }),
        }),
        (0, search_1.searchMcps)({
            repoRoot: cwd,
            query: args.query,
            targets,
            mode: args.mode,
            configuredWorkers: context.configuredWorkers,
            authProfiles: context.authProfiles,
            includeExcluded: args.includeExcluded === true,
            ...(deps.homeDirectory === undefined
                ? {}
                : { homeDirectory: deps.homeDirectory }),
        }),
    ]);
    const result = {
        kind: exports.CAPABILITY_SEARCH_KIND,
        schemaVersion: exports.CAPABILITY_SCHEMA_VERSION,
        repositoryRoot: cwd,
        query: args.query,
        targets,
        mode: args.mode,
        items: sortCapabilitySearchItems([
            ...skillResult.items.map((item) => ({
                type: "skill",
                ...item,
            })),
            ...mcpResult.items.map((item) => ({ type: "mcp", ...item })),
        ], args.query),
        diagnostics: mcpResult.diagnostics,
        errors: [
            ...skillResult.errors.map((error) => ({
                type: "skill",
                ...error,
            })),
            ...mcpResult.errors.map((error) => ({ type: "mcp", ...error })),
        ],
        ...mergeRequirementExclusionHints([
            skillResult.exclusionHint,
            mcpResult.exclusionHint,
        ]),
    };
    (0, json_output_1.printCommandJson)(result);
    return 0;
}
async function loadCapabilityCommandContext(cwd, homeDirectory) {
    let config;
    try {
        config = await (0, control_plane_config_1.loadConfig)(cwd);
    }
    catch (error) {
        if (!(0, fs_errors_1.isMissingFileError)(error)) {
            throw error;
        }
        config = control_plane_config_1.DEFAULT_CONFIG;
    }
    const authProfiles = await (0, store_1.loadAuthProfiles)(homeDirectory);
    return {
        config,
        configuredWorkers: [...(0, control_plane_config_1.getConfiguredWorkerProviders)(config)],
        authProfiles: authProfiles.profiles,
    };
}
function mergeRequirementExclusionHints(hints) {
    const present = hints.filter((hint) => hint !== undefined);
    if (present.length === 0) {
        return {};
    }
    const excludedCount = present.reduce((total, hint) => total + hint.excludedCount, 0);
    const paths = [...new Set(present.flatMap((hint) => hint.paths))].sort();
    const exclusionHint = (0, requirement_exclusions_1.buildRequirementExclusionHint)({
        excludedCount,
        paths,
    });
    return exclusionHint === undefined ? {} : { exclusionHint };
}
function resolveCapabilityTargets(target, configuredWorkers) {
    if (target === "all" || target === undefined) {
        return [...configuredWorkers];
    }
    if (!configuredWorkers.includes(target)) {
        throw new Error(`Worker is not configured: ${target}`);
    }
    return [target];
}
function sortCapabilityListItems(items) {
    return [...items].sort((left, right) => capabilityTypePriority(left.type) - capabilityTypePriority(right.type) ||
        getListWorker(left).localeCompare(getListWorker(right)) ||
        getListRef(left).localeCompare(getListRef(right)) ||
        getListSource(left).localeCompare(getListSource(right)));
}
function sortCapabilitySearchItems(items, query) {
    return [...items].sort((left, right) => (0, search_text_1.scoreSearchText)(right, query) - (0, search_text_1.scoreSearchText)(left, query) ||
        Number(right.installed) - Number(left.installed) ||
        searchSourcePriority(left) - searchSourcePriority(right) ||
        capabilityTypePriority(left.type) - capabilityTypePriority(right.type) ||
        compareQualityMetadata(left, right) ||
        left.id.localeCompare(right.id) ||
        getSearchSource(left).localeCompare(getSearchSource(right)));
}
function capabilityTypePriority(type) {
    return type === "skill" ? 0 : 1;
}
function getListWorker(item) {
    return item.worker;
}
function getListRef(item) {
    return item.type === "skill" ? item.ref : `${item.worker}:${item.serverName}`;
}
function getListSource(item) {
    return item.source;
}
function getSearchSource(item) {
    return item.source;
}
function searchSourcePriority(item) {
    if (item.type === "skill") {
        switch (item.sourceKind) {
            case "local":
                return 0;
            case "codex_builtin":
                return 1;
            case "user_catalog":
                return 2;
            case "system_catalog":
                return 3;
            case "codex_plugin":
                return 4;
            case "claude_plugin":
                return 5;
        }
    }
    switch (item.sourceKind) {
        case "installed":
            return 0;
        case "user_catalog":
            return 2;
        case "system_catalog":
            return 3;
    }
}
function compareQualityMetadata(left, right) {
    return ((right.stars ?? -1) - (left.stars ?? -1) ||
        compareOptionalDateDesc(left.updatedAt, right.updatedAt) ||
        compareOptionalDateAsc(left.createdAt, right.createdAt));
}
function compareOptionalDateDesc(left, right) {
    const leftTimestamp = parseOptionalTimestamp(left);
    const rightTimestamp = parseOptionalTimestamp(right);
    if (leftTimestamp === rightTimestamp) {
        return 0;
    }
    if (leftTimestamp === null) {
        return 1;
    }
    if (rightTimestamp === null) {
        return -1;
    }
    return rightTimestamp - leftTimestamp;
}
function compareOptionalDateAsc(left, right) {
    const leftTimestamp = parseOptionalTimestamp(left);
    const rightTimestamp = parseOptionalTimestamp(right);
    if (leftTimestamp === rightTimestamp) {
        return 0;
    }
    if (leftTimestamp === null) {
        return 1;
    }
    if (rightTimestamp === null) {
        return -1;
    }
    return leftTimestamp - rightTimestamp;
}
function parseOptionalTimestamp(value) {
    if (value === undefined) {
        return null;
    }
    const timestamp = Date.parse(value);
    return Number.isNaN(timestamp) ? null : timestamp;
}
