"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSkillsRouter = createSkillsRouter;
const fs_1 = __importDefault(require("fs"));
const mime_1 = __importDefault(require("mime"));
const express_1 = require("express");
const multer_1 = __importDefault(require("multer"));
const validation_1 = require("../../api/rest/validation");
const index_1 = require("../../api/rest/index");
const manager_types_1 = require("../../graphs/manager-types");
const defaults_1 = require("../../lib/defaults");
const upload = (0, multer_1.default)({ storage: multer_1.default.memoryStorage(), limits: { fileSize: defaults_1.MAX_UPLOAD_SIZE } });
function createSkillsRouter() {
    const router = (0, express_1.Router)({ mergeParams: true });
    function getProject(req) {
        return req.project;
    }
    // List skills
    router.get('/', (0, validation_1.validateQuery)(validation_1.skillListSchema), (req, res, next) => {
        try {
            const p = getProject(req);
            const q = req.validatedQuery;
            const { results: skills, total } = p.skillManager.listSkills(q);
            res.json({ results: skills, total });
        }
        catch (err) {
            next(err);
        }
    });
    // Search skills
    router.get('/search', (0, validation_1.validateQuery)(validation_1.skillSearchSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const q = req.validatedQuery;
            const results = await p.skillManager.searchSkills(q.q, {
                topK: q.topK,
                minScore: q.minScore,
                searchMode: q.searchMode,
                bfsDepth: q.bfsDepth,
                maxResults: q.maxResults,
                bfsDecay: q.bfsDecay,
            });
            res.json({ results });
        }
        catch (err) {
            next(err);
        }
    });
    // Recall skills (higher recall search for task contexts)
    router.get('/recall', (0, validation_1.validateQuery)(validation_1.skillSearchSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const q = req.validatedQuery;
            const results = await p.skillManager.searchSkills(q.q, {
                topK: q.topK,
                minScore: q.minScore ?? 0.3,
                searchMode: q.searchMode,
                bfsDepth: q.bfsDepth,
                maxResults: q.maxResults,
                bfsDecay: q.bfsDecay,
            });
            res.json({ results });
        }
        catch (err) {
            next(err);
        }
    });
    // Find skills linked to an external entity
    router.get('/linked', (0, validation_1.validateQuery)(validation_1.linkedQuerySchema), (req, res, next) => {
        try {
            const p = getProject(req);
            const { targetGraph, targetNodeId, kind, projectId } = req.validatedQuery;
            const skills = p.skillManager.findLinkedSkills(targetGraph, targetNodeId, kind, projectId ?? req.params.projectId);
            res.json({ results: skills });
        }
        catch (err) {
            next(err);
        }
    });
    // Get skill
    router.get('/:skillId', (req, res, next) => {
        try {
            const p = getProject(req);
            const skill = p.skillManager.getSkill(req.params.skillId);
            if (!skill)
                return res.status(404).json({ error: 'Skill not found' });
            const relations = p.skillManager.listRelations(req.params.skillId);
            res.json({ ...skill, relations });
        }
        catch (err) {
            next(err);
        }
    });
    // Create skill
    router.post('/', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.createSkillSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const { title, description, steps, triggers, inputHints, filePatterns, tags, source, confidence } = req.body;
            const created = await p.mutationQueue.enqueue(async () => {
                const skillId = await p.skillManager.createSkill(title, description, steps, triggers, inputHints, filePatterns, tags, source, confidence);
                return p.skillManager.getSkill(skillId);
            });
            res.status(201).json(created);
        }
        catch (err) {
            next(err);
        }
    });
    // Update skill
    router.put('/:skillId', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.updateSkillSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const skillId = req.params.skillId;
            const { version, ...patch } = req.body;
            const result = await p.mutationQueue.enqueue(async () => {
                const ok = await p.skillManager.updateSkill(skillId, patch, version);
                if (!ok)
                    return null;
                return p.skillManager.getSkill(skillId);
            });
            if (!result)
                return res.status(404).json({ error: 'Skill not found' });
            res.json(result);
        }
        catch (err) {
            if (err instanceof manager_types_1.VersionConflictError) {
                return res.status(409).json({ error: 'version_conflict', current: err.current, expected: err.expected });
            }
            next(err);
        }
    });
    // Bump usage
    router.post('/:skillId/bump', index_1.requireWriteAccess, async (req, res, next) => {
        try {
            const p = getProject(req);
            const skillId = req.params.skillId;
            const result = await p.mutationQueue.enqueue(async () => {
                const ok = p.skillManager.bumpUsage(skillId);
                if (!ok)
                    return null;
                return p.skillManager.getSkill(skillId);
            });
            if (!result)
                return res.status(404).json({ error: 'Skill not found' });
            res.json(result);
        }
        catch (err) {
            next(err);
        }
    });
    // Delete skill
    router.delete('/:skillId', index_1.requireWriteAccess, async (req, res, next) => {
        try {
            const p = getProject(req);
            const skillId = req.params.skillId;
            const ok = await p.mutationQueue.enqueue(async () => {
                return p.skillManager.deleteSkill(skillId);
            });
            if (!ok)
                return res.status(404).json({ error: 'Skill not found' });
            res.status(204).end();
        }
        catch (err) {
            next(err);
        }
    });
    // Create skill link (skill-to-skill or cross-graph)
    router.post('/links', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.createSkillLinkSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const { fromId, toId, kind, targetGraph, projectId } = req.body;
            const ok = await p.mutationQueue.enqueue(async () => {
                if (targetGraph) {
                    return p.skillManager.createCrossLink(fromId, toId, targetGraph, kind, projectId);
                }
                else {
                    return p.skillManager.linkSkills(fromId, toId, kind);
                }
            });
            if (!ok)
                return res.status(400).json({ error: 'Failed to create link' });
            res.status(201).json({ fromId, toId, kind, targetGraph: targetGraph || undefined });
        }
        catch (err) {
            next(err);
        }
    });
    // Delete skill link
    router.delete('/links', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.createSkillLinkSchema.pick({ fromId: true, toId: true, targetGraph: true, projectId: true })), async (req, res, next) => {
        try {
            const p = getProject(req);
            const { fromId, toId, targetGraph, projectId } = req.body;
            const ok = await p.mutationQueue.enqueue(async () => {
                if (targetGraph) {
                    return p.skillManager.deleteCrossLink(fromId, toId, targetGraph, projectId);
                }
                else {
                    return p.skillManager.deleteSkillLink(fromId, toId);
                }
            });
            if (!ok)
                return res.status(404).json({ error: 'Link not found' });
            res.status(204).end();
        }
        catch (err) {
            next(err);
        }
    });
    // List relations for a skill
    router.get('/:skillId/relations', (req, res, next) => {
        try {
            const p = getProject(req);
            const relations = p.skillManager.listRelations(req.params.skillId);
            res.json({ results: relations });
        }
        catch (err) {
            next(err);
        }
    });
    // -- Attachments --
    // Upload attachment
    router.post('/:skillId/attachments', index_1.requireWriteAccess, upload.single('file'), async (req, res, next) => {
        try {
            const p = getProject(req);
            const skillId = req.params.skillId;
            const file = req.file;
            if (!file)
                return res.status(400).json({ error: 'No file uploaded' });
            const filename = validation_1.attachmentFilenameSchema.parse(file.originalname);
            const meta = await p.mutationQueue.enqueue(async () => {
                return p.skillManager.addAttachment(skillId, filename, file.buffer);
            });
            if (!meta)
                return res.status(404).json({ error: 'Skill not found' });
            res.status(201).json(meta);
        }
        catch (err) {
            next(err);
        }
    });
    // List attachments
    router.get('/:skillId/attachments', (req, res, next) => {
        try {
            const p = getProject(req);
            const attachments = p.skillManager.listAttachments(req.params.skillId);
            res.json({ results: attachments });
        }
        catch (err) {
            next(err);
        }
    });
    // Download attachment
    router.get('/:skillId/attachments/:filename', (req, res, next) => {
        try {
            const p = getProject(req);
            const filename = validation_1.attachmentFilenameSchema.parse(req.params.filename);
            const filePath = p.skillManager.getAttachmentPath(req.params.skillId, filename);
            if (!filePath)
                return res.status(404).json({ error: 'Attachment not found' });
            const mimeType = mime_1.default.getType(filePath) ?? 'application/octet-stream';
            res.setHeader('Content-Type', mimeType);
            res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`);
            res.setHeader('X-Content-Type-Options', 'nosniff');
            const stream = fs_1.default.createReadStream(filePath);
            stream.on('error', (err) => next(err));
            stream.pipe(res);
        }
        catch (err) {
            next(err);
        }
    });
    // Delete attachment
    router.delete('/:skillId/attachments/:filename', index_1.requireWriteAccess, async (req, res, next) => {
        try {
            const p = getProject(req);
            const skillId = req.params.skillId;
            const filename = validation_1.attachmentFilenameSchema.parse(req.params.filename);
            const ok = await p.mutationQueue.enqueue(async () => {
                return p.skillManager.removeAttachment(skillId, filename);
            });
            if (!ok)
                return res.status(404).json({ error: 'Attachment not found' });
            res.status(204).end();
        }
        catch (err) {
            next(err);
        }
    });
    return router;
}
