"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const skillLoader_1 = require("../services/skillEngine/skillLoader");
const httpValue_1 = require("../utils/httpValue");
const SKILLS_DIR = (0, skillLoader_1.getSkillsDir)();
const COMPOSITE_DIR = path_1.default.join(SKILLS_DIR, 'composite');
const VENDORS_DIR = path_1.default.join(SKILLS_DIR, 'vendors');
const CUSTOM_DIR = path_1.default.join(SKILLS_DIR, 'custom');
class SkillAdminController {
    constructor() {
        /**
         * List all skills with admin metadata
         * GET /api/admin/skills
         */
        this.listSkills = async (req, res) => {
            try {
                await this.ensureInitialized();
                const skills = skillLoader_1.skillRegistry.getAllSkills();
                const result = skills.map(skill => ({
                    id: skill.name,
                    name: skill.name,
                    version: skill.version,
                    displayName: skill.meta?.display_name || skill.name,
                    description: skill.meta?.description || '',
                    category: skill.category,
                    type: skill.type,
                    stepsCount: skill.steps?.length || 0,
                    tags: skill.meta?.tags,
                }));
                res.json({
                    skills: result,
                    count: result.length,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error listing skills:', error);
                const errorResponse = {
                    error: 'Failed to list skills',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * Get skill details including raw YAML
         * GET /api/admin/skills/:skillId
         */
        this.getSkill = async (req, res) => {
            try {
                const skillId = (0, httpValue_1.toSingleString)(req.params.skillId);
                await this.ensureInitialized();
                if (!skillId) {
                    return res.status(400).json({
                        error: 'Missing skill ID',
                        details: 'skillId is required',
                    });
                }
                const skill = skillLoader_1.skillRegistry.getSkill(skillId);
                if (!skill) {
                    return res.status(404).json({
                        error: 'Skill not found',
                        details: `No skill found with ID: ${skillId}`,
                    });
                }
                // Try to find the YAML file
                const possiblePaths = [
                    path_1.default.join(COMPOSITE_DIR, `${skillId}.skill.yaml`),
                    path_1.default.join(SKILLS_DIR, 'atomic', `${skillId}.skill.yaml`),
                    path_1.default.join(CUSTOM_DIR, `${skillId}.skill.yaml`),
                ];
                let rawYaml = '';
                let filePath = '';
                for (const p of possiblePaths) {
                    if (fs_1.default.existsSync(p)) {
                        rawYaml = fs_1.default.readFileSync(p, 'utf-8');
                        filePath = p;
                        break;
                    }
                }
                res.json({
                    id: skill.name,
                    definition: skill,
                    rawYaml,
                    filePath,
                    isCustom: filePath.includes('/custom/'),
                    isEditable: filePath.includes('/custom/'),
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error getting skill:', error);
                const errorResponse = {
                    error: 'Failed to get skill',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * Create a new custom skill
         * POST /api/admin/skills
         * Body: { yaml: string } or { definition: SkillDefinition }
         */
        this.createSkill = async (req, res) => {
            try {
                const { yaml: yamlContent, definition } = req.body;
                let skillDef;
                let yamlToSave;
                if (yamlContent) {
                    // Parse YAML
                    skillDef = js_yaml_1.default.load(yamlContent);
                    yamlToSave = yamlContent;
                }
                else if (definition) {
                    skillDef = definition;
                    yamlToSave = js_yaml_1.default.dump(definition);
                }
                else {
                    return res.status(400).json({
                        error: 'Missing skill data',
                        details: 'Either yaml or definition is required',
                    });
                }
                // Validate required fields
                if (!skillDef.name) {
                    return res.status(400).json({
                        error: 'Invalid skill definition',
                        details: 'Skill name is required',
                    });
                }
                // Ensure custom directory exists
                if (!fs_1.default.existsSync(CUSTOM_DIR)) {
                    fs_1.default.mkdirSync(CUSTOM_DIR, { recursive: true });
                }
                // Check if skill already exists
                const fileName = `${skillDef.name}.skill.yaml`;
                const filePath = path_1.default.join(CUSTOM_DIR, fileName);
                if (fs_1.default.existsSync(filePath)) {
                    return res.status(409).json({
                        error: 'Skill already exists',
                        details: `A skill with name '${skillDef.name}' already exists`,
                    });
                }
                // Save file
                fs_1.default.writeFileSync(filePath, yamlToSave, 'utf-8');
                // Reload skills
                await skillLoader_1.skillRegistry.reload();
                res.status(201).json({
                    success: true,
                    message: 'Skill created successfully',
                    skillId: skillDef.name,
                    filePath,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error creating skill:', error);
                const errorResponse = {
                    error: 'Failed to create skill',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * Update an existing custom skill
         * PUT /api/admin/skills/:skillId
         * Body: { yaml: string } or { definition: SkillDefinition }
         */
        this.updateSkill = async (req, res) => {
            try {
                const skillId = (0, httpValue_1.toSingleString)(req.params.skillId);
                const { yaml: yamlContent, definition } = req.body;
                if (!skillId) {
                    return res.status(400).json({
                        error: 'Missing skill ID',
                        details: 'skillId is required',
                    });
                }
                await this.ensureInitialized();
                const skill = skillLoader_1.skillRegistry.getSkill(skillId);
                if (!skill) {
                    return res.status(404).json({
                        error: 'Skill not found',
                        details: `No skill found with ID: ${skillId}`,
                    });
                }
                // Find the file path - only custom skills are editable
                const customFilePath = path_1.default.join(CUSTOM_DIR, `${skillId}.skill.yaml`);
                if (!fs_1.default.existsSync(customFilePath)) {
                    return res.status(403).json({
                        error: 'Skill not editable',
                        details: 'Only custom skills can be edited. Base and vendor skills are read-only.',
                    });
                }
                const filePath = customFilePath;
                let yamlToSave;
                if (yamlContent) {
                    yamlToSave = yamlContent;
                }
                else if (definition) {
                    yamlToSave = js_yaml_1.default.dump(definition);
                }
                else {
                    return res.status(400).json({
                        error: 'Missing skill data',
                        details: 'Either yaml or definition is required',
                    });
                }
                // Save file
                fs_1.default.writeFileSync(filePath, yamlToSave, 'utf-8');
                // Reload skills
                await skillLoader_1.skillRegistry.reload();
                res.json({
                    success: true,
                    message: 'Skill updated successfully',
                    skillId,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error updating skill:', error);
                const errorResponse = {
                    error: 'Failed to update skill',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * Delete a custom skill
         * DELETE /api/admin/skills/:skillId
         */
        this.deleteSkill = async (req, res) => {
            try {
                const skillId = (0, httpValue_1.toSingleString)(req.params.skillId);
                if (!skillId) {
                    return res.status(400).json({
                        error: 'Missing skill ID',
                        details: 'skillId is required',
                    });
                }
                await this.ensureInitialized();
                const skill = skillLoader_1.skillRegistry.getSkill(skillId);
                if (!skill) {
                    return res.status(404).json({
                        error: 'Skill not found',
                        details: `No skill found with ID: ${skillId}`,
                    });
                }
                // Check if deletable (only custom skills)
                const customFilePath = path_1.default.join(CUSTOM_DIR, `${skillId}.skill.yaml`);
                if (!fs_1.default.existsSync(customFilePath)) {
                    return res.status(403).json({
                        error: 'Skill not deletable',
                        details: 'Only custom skills can be deleted. Base and vendor skills are protected.',
                    });
                }
                // Delete file
                fs_1.default.unlinkSync(customFilePath);
                // Also delete SOP if exists
                const sopPath = customFilePath.replace('.skill.yaml', '.sop.md');
                if (fs_1.default.existsSync(sopPath)) {
                    fs_1.default.unlinkSync(sopPath);
                }
                // Reload skills
                await skillLoader_1.skillRegistry.reload();
                res.json({
                    success: true,
                    message: 'Skill deleted successfully',
                    skillId,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error deleting skill:', error);
                const errorResponse = {
                    error: 'Failed to delete skill',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * Validate skill YAML
         * POST /api/admin/skills/validate
         * Body: { yaml: string }
         */
        this.validateSkill = async (req, res) => {
            try {
                const { yaml: yamlContent } = req.body;
                if (!yamlContent) {
                    return res.status(400).json({
                        error: 'Missing YAML content',
                        details: 'yaml is required',
                    });
                }
                const errors = [];
                const warnings = [];
                // Parse YAML
                let skillDef;
                try {
                    skillDef = js_yaml_1.default.load(yamlContent);
                }
                catch (e) {
                    return res.json({
                        valid: false,
                        errors: [`YAML parse error: ${e.message}`],
                        warnings: [],
                    });
                }
                // Validate required fields
                if (!skillDef.name)
                    errors.push('Missing required field: name');
                if (!skillDef.version)
                    errors.push('Missing required field: version');
                if (!skillDef.meta)
                    errors.push('Missing required field: meta');
                if (!skillDef.steps || skillDef.steps.length === 0) {
                    errors.push('Missing required field: steps');
                }
                // Validate meta
                if (skillDef.meta) {
                    if (!skillDef.meta.display_name)
                        errors.push('Missing meta.display_name');
                    if (!skillDef.meta.description)
                        errors.push('Missing meta.description');
                }
                // Validate steps
                if (skillDef.steps) {
                    const stepIds = new Set();
                    skillDef.steps.forEach((step, i) => {
                        if (!step.id)
                            errors.push(`steps[${i}]: Missing id`);
                        // name is optional for most step types
                        // sql is only required for atomic steps
                        if ('type' in step && step.type === 'atomic' && !('sql' in step && step.sql)) {
                            errors.push(`steps[${i}]: Atomic step missing sql`);
                        }
                        if (step.id && stepIds.has(step.id)) {
                            errors.push(`steps[${i}]: Duplicate id '${step.id}'`);
                        }
                        if (step.id)
                            stepIds.add(step.id);
                    });
                }
                res.json({
                    valid: errors.length === 0,
                    errors,
                    warnings,
                    parsedDefinition: errors.length === 0 ? skillDef : undefined,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error validating skill:', error);
                const errorResponse = {
                    error: 'Failed to validate skill',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * List all vendors
         * GET /api/admin/vendors
         */
        this.listVendors = async (req, res) => {
            try {
                if (!fs_1.default.existsSync(VENDORS_DIR)) {
                    return res.json({ vendors: [] });
                }
                const vendorDirs = fs_1.default.readdirSync(VENDORS_DIR).filter(f => fs_1.default.statSync(path_1.default.join(VENDORS_DIR, f)).isDirectory());
                const vendors = vendorDirs.map(vendor => {
                    const vendorPath = path_1.default.join(VENDORS_DIR, vendor);
                    const overrides = fs_1.default.readdirSync(vendorPath).filter(f => f.endsWith('.override.yaml'));
                    return {
                        id: vendor,
                        overrideCount: overrides.length,
                        overrides: overrides.map(f => f.replace('.override.yaml', '')),
                    };
                });
                res.json({
                    vendors,
                    count: vendors.length,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error listing vendors:', error);
                const errorResponse = {
                    error: 'Failed to list vendors',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * Get vendor overrides
         * GET /api/admin/vendors/:vendor/overrides
         */
        this.getVendorOverrides = async (req, res) => {
            try {
                const vendor = (0, httpValue_1.toSingleString)(req.params.vendor);
                if (!vendor) {
                    return res.status(400).json({
                        error: 'Missing vendor',
                        details: 'vendor is required',
                    });
                }
                const vendorPath = path_1.default.join(VENDORS_DIR, vendor);
                if (!fs_1.default.existsSync(vendorPath)) {
                    return res.status(404).json({
                        error: 'Vendor not found',
                        details: `No vendor found: ${vendor}`,
                    });
                }
                const overrideFiles = fs_1.default.readdirSync(vendorPath).filter(f => f.endsWith('.override.yaml'));
                const overrides = overrideFiles.map(file => {
                    const filePath = path_1.default.join(vendorPath, file);
                    const content = fs_1.default.readFileSync(filePath, 'utf-8');
                    const override = js_yaml_1.default.load(content);
                    return {
                        skillId: file.replace('.override.yaml', ''),
                        extends: override.extends,
                        version: override.version,
                        meta: override.meta,
                        additionalStepsCount: override.additional_steps?.length || 0,
                        rawYaml: content,
                    };
                });
                res.json({
                    vendor,
                    overrides,
                    count: overrides.length,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error getting vendor overrides:', error);
                const errorResponse = {
                    error: 'Failed to get vendor overrides',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
        /**
         * Reload all skills
         * POST /api/admin/skills/reload
         */
        this.reloadSkills = async (req, res) => {
            try {
                await skillLoader_1.skillRegistry.reload();
                const skills = skillLoader_1.skillRegistry.getAllSkills();
                res.json({
                    success: true,
                    message: 'Skills reloaded successfully',
                    count: skills.length,
                });
            }
            catch (error) {
                console.error('[SkillAdminController] Error reloading skills:', error);
                const errorResponse = {
                    error: 'Failed to reload skills',
                    details: error instanceof Error ? error.message : 'Unknown error',
                };
                res.status(500).json(errorResponse);
            }
        };
    }
    /**
     * Ensure skill registry is initialized
     */
    async ensureInitialized() {
        await (0, skillLoader_1.ensureSkillRegistryInitialized)();
    }
}
exports.default = SkillAdminController;
//# sourceMappingURL=skillAdminController.js.map