"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createKnowledgeRouter = createKnowledgeRouter;
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 createKnowledgeRouter() {
    const router = (0, express_1.Router)({ mergeParams: true });
    function getProject(req) {
        return req.project;
    }
    // List notes
    router.get('/notes', (0, validation_1.validateQuery)(validation_1.noteListSchema), (req, res, next) => {
        try {
            const p = getProject(req);
            const q = req.validatedQuery;
            const { results: notes, total } = p.knowledgeManager.listNotes(q.filter, q.tag, q.limit, q.offset);
            res.json({ results: notes, total });
        }
        catch (err) {
            next(err);
        }
    });
    // Search notes
    router.get('/search', (0, validation_1.validateQuery)(validation_1.noteSearchSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const q = req.validatedQuery;
            const results = await p.knowledgeManager.searchNotes(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);
        }
    });
    // Get note
    router.get('/notes/:noteId', (req, res, next) => {
        try {
            const p = getProject(req);
            const note = p.knowledgeManager.getNote(req.params.noteId);
            if (!note)
                return res.status(404).json({ error: 'Note not found' });
            const { embedding: _, ...rest } = note;
            res.json(rest);
        }
        catch (err) {
            next(err);
        }
    });
    // Create note
    router.post('/notes', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.createNoteSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const { title, content, tags } = req.body;
            const created = await p.mutationQueue.enqueue(async () => {
                const noteId = await p.knowledgeManager.createNote(title, content, tags);
                return p.knowledgeManager.getNote(noteId);
            });
            res.status(201).json(created);
        }
        catch (err) {
            next(err);
        }
    });
    // Update note
    router.put('/notes/:noteId', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.updateNoteSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const noteId = req.params.noteId;
            const { version, ...patch } = req.body;
            const result = await p.mutationQueue.enqueue(async () => {
                const ok = await p.knowledgeManager.updateNote(noteId, patch, version);
                if (!ok)
                    return null;
                return p.knowledgeManager.getNote(noteId);
            });
            if (!result)
                return res.status(404).json({ error: 'Note 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);
        }
    });
    // Delete note
    router.delete('/notes/:noteId', index_1.requireWriteAccess, async (req, res, next) => {
        try {
            const p = getProject(req);
            const noteId = req.params.noteId;
            const ok = await p.mutationQueue.enqueue(async () => {
                return p.knowledgeManager.deleteNote(noteId);
            });
            if (!ok)
                return res.status(404).json({ error: 'Note not found' });
            res.status(204).end();
        }
        catch (err) {
            next(err);
        }
    });
    // Create relation
    router.post('/relations', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.createRelationSchema), async (req, res, next) => {
        try {
            const p = getProject(req);
            const { fromId, toId, kind, targetGraph, projectId } = req.body;
            const ok = await p.mutationQueue.enqueue(async () => {
                return p.knowledgeManager.createRelation(fromId, toId, kind, targetGraph, projectId);
            });
            if (!ok)
                return res.status(400).json({ error: 'Failed to create relation' });
            res.status(201).json({ fromId, toId, kind, targetGraph: targetGraph || undefined });
        }
        catch (err) {
            next(err);
        }
    });
    // Delete relation
    router.delete('/relations', index_1.requireWriteAccess, (0, validation_1.validateBody)(validation_1.createRelationSchema.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 () => {
                return p.knowledgeManager.deleteRelation(fromId, toId, targetGraph, projectId);
            });
            if (!ok)
                return res.status(404).json({ error: 'Relation not found' });
            res.status(204).end();
        }
        catch (err) {
            next(err);
        }
    });
    // List relations for a note
    router.get('/notes/:noteId/relations', (req, res, next) => {
        try {
            const p = getProject(req);
            const relations = p.knowledgeManager.listRelations(req.params.noteId);
            res.json({ results: relations });
        }
        catch (err) {
            next(err);
        }
    });
    // Find notes 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 notes = p.knowledgeManager.findLinkedNotes(targetGraph, targetNodeId, kind, projectId ?? req.params.projectId);
            res.json({ results: notes });
        }
        catch (err) {
            next(err);
        }
    });
    // -- Attachments --
    // Upload attachment
    router.post('/notes/:noteId/attachments', index_1.requireWriteAccess, upload.single('file'), async (req, res, next) => {
        try {
            const p = getProject(req);
            const noteId = req.params.noteId;
            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.knowledgeManager.addAttachment(noteId, filename, file.buffer);
            });
            if (!meta)
                return res.status(404).json({ error: 'Note not found' });
            res.status(201).json(meta);
        }
        catch (err) {
            next(err);
        }
    });
    // List attachments
    router.get('/notes/:noteId/attachments', (req, res, next) => {
        try {
            const p = getProject(req);
            const attachments = p.knowledgeManager.listAttachments(req.params.noteId);
            res.json({ results: attachments });
        }
        catch (err) {
            next(err);
        }
    });
    // Download attachment
    router.get('/notes/:noteId/attachments/:filename', (req, res, next) => {
        try {
            const p = getProject(req);
            const filename = validation_1.attachmentFilenameSchema.parse(req.params.filename);
            const filePath = p.knowledgeManager.getAttachmentPath(req.params.noteId, 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('/notes/:noteId/attachments/:filename', index_1.requireWriteAccess, async (req, res, next) => {
        try {
            const p = getProject(req);
            const noteId = req.params.noteId;
            const filename = validation_1.attachmentFilenameSchema.parse(req.params.filename);
            const ok = await p.mutationQueue.enqueue(async () => {
                return p.knowledgeManager.removeAttachment(noteId, filename);
            });
            if (!ok)
                return res.status(404).json({ error: 'Attachment not found' });
            res.status(204).end();
        }
        catch (err) {
            next(err);
        }
    });
    return router;
}
