"use strict";
/**
 * CapabilityPackParser — load + validate capability pack YAML manifests.
 *
 * Story 1.1 — OpenLife v1.3 Agent OS Integration.
 *
 * Mirrors WorkflowParser's pattern: never throws on user input, returns
 * `{ ok, pack?, errors[] }` with stable error codes from CAPABILITY_ERRORS.
 *
 * Validation scope (what this parser CAN check):
 *   - YAML well-formedness
 *   - Required scalar fields (id, name, description, version, status, objective)
 *   - Status enum membership (draft|tested|active|deprecated)
 *   - Asset-ref shape (id+source on each item)
 *   - Duplicate asset ids within each kind (agents/skills/squads/workflows)
 *   - Embedded-ref file existence (when given a directory context)
 *
 * Validation scope (what this parser CANNOT check — done by the registry layer):
 *   - Whether `referenced` asset ids resolve in the global agent/skill/squad/workflow registries
 *   - Whether the pack name conflicts with existing packs
 *   These higher-level checks live in CapabilityPackState (Story 1.4).
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseCapabilityPack = parseCapabilityPack;
exports.parseCapabilityPackFile = parseCapabilityPackFile;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const yaml = __importStar(require("js-yaml"));
const CapabilityPackSchema_1 = require("./CapabilityPackSchema");
/** Parse a YAML string. Never throws. */
function parseCapabilityPack(yamlContent, opts = {}) {
    const errors = [];
    let doc;
    try {
        doc = yaml.load(yamlContent);
    }
    catch (err) {
        return {
            ok: false,
            errors: [{
                    code: CapabilityPackSchema_1.CAPABILITY_ERRORS.INVALID_YAML,
                    message: err instanceof Error ? err.message : 'YAML parse failed',
                }],
        };
    }
    if (doc === null || typeof doc !== 'object') {
        return {
            ok: false,
            errors: [{
                    code: CapabilityPackSchema_1.CAPABILITY_ERRORS.MISSING_ROOT,
                    message: 'document must be a YAML mapping with a `capability:` root key',
                }],
        };
    }
    const root = doc;
    if (!root.capability || typeof root.capability !== 'object') {
        return {
            ok: false,
            errors: [{
                    code: CapabilityPackSchema_1.CAPABILITY_ERRORS.MISSING_ROOT,
                    message: 'top-level `capability:` key is required',
                }],
        };
    }
    const cap = root.capability;
    // Required scalars
    requireString(cap, 'id', errors);
    requireString(cap, 'name', errors);
    requireString(cap, 'description', errors);
    requireString(cap, 'version', errors);
    requireString(cap, 'objective', errors);
    // Status — must be one of the four valid values
    if (typeof cap.status !== 'string') {
        errors.push({
            code: CapabilityPackSchema_1.CAPABILITY_ERRORS.MISSING_REQUIRED_FIELD,
            message: '`status` is required (draft | tested | active | deprecated)',
        });
    }
    else if (!(0, CapabilityPackSchema_1.isCapabilityStatus)(cap.status)) {
        errors.push({
            code: CapabilityPackSchema_1.CAPABILITY_ERRORS.INVALID_STATUS,
            message: 'status must be one of: draft, tested, active, deprecated',
            context: { received: cap.status },
        });
    }
    // Asset refs — validate shape + de-duplicate per kind
    validateAssetRefs(cap.agents, 'agents', errors);
    validateAssetRefs(cap.skills, 'skills', errors);
    validateAssetRefs(cap.squads, 'squads', errors);
    validateAssetRefs(cap.workflows, 'workflows', errors);
    // Embedded-ref file existence (when packDir is provided)
    if (opts.packDir) {
        validateEmbeddedFiles(cap.agents, 'agents', opts.packDir, errors);
        validateEmbeddedFiles(cap.skills, 'skills', opts.packDir, errors);
        validateEmbeddedFiles(cap.squads, 'squads', opts.packDir, errors);
        validateEmbeddedFiles(cap.workflows, 'workflows', opts.packDir, errors);
    }
    if (errors.length > 0) {
        return { ok: false, errors };
    }
    return { ok: true, pack: cap, errors: [] };
}
/** Parse a YAML file by path. Convenience wrapper. */
function parseCapabilityPackFile(filePath) {
    if (!fs.existsSync(filePath)) {
        return {
            ok: false,
            errors: [{
                    code: CapabilityPackSchema_1.CAPABILITY_ERRORS.INVALID_YAML,
                    message: `capability manifest not found: ${filePath}`,
                    context: { filePath },
                }],
        };
    }
    const content = fs.readFileSync(filePath, 'utf-8');
    return parseCapabilityPack(content, { packDir: path.dirname(filePath) });
}
// ─── internal helpers ──────────────────────────────────────────────
function requireString(obj, field, errors) {
    const v = obj[field];
    if (typeof v !== 'string' || v.length === 0) {
        errors.push({
            code: CapabilityPackSchema_1.CAPABILITY_ERRORS.MISSING_REQUIRED_FIELD,
            message: `\`${field}\` is required and must be a non-empty string`,
        });
    }
}
function validateAssetRefs(refs, kind, errors) {
    if (refs === undefined)
        return;
    if (!Array.isArray(refs)) {
        errors.push({
            code: CapabilityPackSchema_1.CAPABILITY_ERRORS.INVALID_TYPE,
            message: `\`${kind}\` must be an array of asset refs`,
        });
        return;
    }
    const seen = new Set();
    for (let i = 0; i < refs.length; i++) {
        const ref = refs[i];
        if (typeof ref?.id !== 'string' || ref.id.length === 0) {
            errors.push({
                code: CapabilityPackSchema_1.CAPABILITY_ERRORS.MISSING_REQUIRED_FIELD,
                message: `${kind}[${i}].id is required`,
            });
            continue;
        }
        if (ref.source !== 'embedded' && ref.source !== 'referenced') {
            errors.push({
                code: CapabilityPackSchema_1.CAPABILITY_ERRORS.INVALID_TYPE,
                message: `${kind}[${i}].source must be 'embedded' or 'referenced'`,
                context: { id: ref.id, received: String(ref.source) },
            });
        }
        if (seen.has(ref.id)) {
            errors.push({
                code: CapabilityPackSchema_1.CAPABILITY_ERRORS.DUPLICATE_ASSET_REF,
                message: `${kind}: duplicate asset id '${ref.id}'`,
                context: { kind, id: ref.id },
            });
        }
        seen.add(ref.id);
    }
}
function validateEmbeddedFiles(refs, kind, packDir, errors) {
    if (!Array.isArray(refs))
        return;
    for (const ref of refs) {
        if (ref?.source !== 'embedded' || typeof ref.id !== 'string')
            continue;
        // Convention: embedded assets live under <packDir>/<kind>/<id>/ (squads, agents)
        // or <packDir>/<kind>/<id>.{md,yaml} (skills, workflows). Accept any of:
        //   <kind>/<id>/             (dir)
        //   <kind>/<id>.md           (single-file skill)
        //   <kind>/<id>.yaml         (single-file workflow)
        const candidates = [
            path.join(packDir, kind, ref.id),
            path.join(packDir, kind, `${ref.id}.md`),
            path.join(packDir, kind, `${ref.id}.yaml`),
            path.join(packDir, kind, `${ref.id}.yml`),
        ];
        if (!candidates.some((p) => fs.existsSync(p))) {
            errors.push({
                code: CapabilityPackSchema_1.CAPABILITY_ERRORS.EMBEDDED_REF_MISSING_FILE,
                message: `${kind} embedded ref '${ref.id}' has no file under ${path.join(kind, ref.id)}`,
                context: { kind, id: ref.id },
            });
        }
    }
}
