/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { createHash } from 'node:crypto';
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
import { extname, join, resolve, relative } from 'node:path';
import { Hono } from 'hono';
import { getWorkspaceCwd } from '../middleware/workspace.js';
function classifyFile(filename) {
    const ext = extname(filename).toLowerCase();
    switch (ext) {
        case '.md':
            return 'md';
        case '.json':
        case '.jsonl':
            return 'json';
        case '.yaml':
        case '.yml':
            return 'yaml';
        case '.drawio':
            return 'drawio';
        default:
            return 'other';
    }
}
function walkDir(dir, baseDir) {
    const results = [];
    if (!existsSync(dir))
        return results;
    const entries = readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
        const fullPath = join(dir, entry.name);
        if (entry.isDirectory()) {
            results.push(...walkDir(fullPath, baseDir));
        }
        else if (entry.isFile()) {
            const stat = statSync(fullPath);
            const relPath = relative(baseDir, fullPath);
            results.push({
                path: relPath,
                name: entry.name,
                type: classifyFile(entry.name),
                size: stat.size,
                modified: stat.mtime.toISOString(),
            });
        }
    }
    return results;
}
function computeEtag(content) {
    return createHash('sha256').update(content).digest('hex');
}
export function artifactsRoute(cwd) {
    const app = new Hono();
    app.get('/', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const workflowDir = join(dir, '.agent-workflow');
        const setupDir = join(dir, '.setup-agents');
        const workflowFiles = walkDir(workflowDir, workflowDir).map((f) => ({
            ...f,
            path: `.agent-workflow/${f.path}`,
        }));
        const setupFiles = walkDir(setupDir, setupDir).map((f) => ({
            ...f,
            path: `.setup-agents/${f.path}`,
        }));
        return c.json({ files: [...workflowFiles, ...setupFiles] });
    });
    app.get('/:path{.+}', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const filePath = c.req.param('path');
        if (!filePath) {
            return c.json({ error: 'No file path provided' }, 400);
        }
        // Only allow access to known artifact directories
        const allowedPrefixes = ['.agent-workflow/', '.setup-agents/'];
        if (!allowedPrefixes.some((p) => filePath.startsWith(p))) {
            return c.json({ error: 'Forbidden' }, 403);
        }
        // Path traversal protection
        const resolved = resolve(dir, filePath);
        if (!resolved.startsWith(dir)) {
            return c.json({ error: 'Forbidden' }, 403);
        }
        if (!existsSync(resolved)) {
            return c.json({ error: 'Not found' }, 404);
        }
        const stat = statSync(resolved);
        if (!stat.isFile()) {
            return c.json({ error: 'Not a file' }, 400);
        }
        const content = readFileSync(resolved, 'utf-8');
        const hash = computeEtag(content);
        const type = classifyFile(resolved);
        c.header('ETag', `"${hash}"`);
        return c.json({ content, type });
    });
    app.put('/:path{.+}', async (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const filePath = c.req.param('path');
        if (!filePath) {
            return c.json({ error: 'No file path provided' }, 400);
        }
        // Only allow writes to known artifact directories
        const allowedPrefixes = ['.agent-workflow/', '.setup-agents/'];
        if (!allowedPrefixes.some((p) => filePath.startsWith(p))) {
            return c.json({ error: 'Forbidden' }, 403);
        }
        // Path traversal protection
        const resolved = resolve(dir, filePath);
        if (!resolved.startsWith(dir)) {
            return c.json({ error: 'Forbidden' }, 403);
        }
        // Parse body
        let newContent;
        const contentType = c.req.header('Content-Type') ?? '';
        if (contentType.includes('application/json')) {
            const body = await c.req.json();
            if (!body.content && body.content !== '') {
                return c.json({ error: 'Body must contain a "content" field' }, 400);
            }
            newContent = body.content;
        }
        else {
            newContent = await c.req.text();
        }
        // ETag-based conflict detection
        const ifMatch = c.req.header('If-Match');
        if (ifMatch && existsSync(resolved)) {
            const currentContent = readFileSync(resolved, 'utf-8');
            const currentEtag = `"${computeEtag(currentContent)}"`;
            if (ifMatch !== currentEtag) {
                return c.json({ error: 'Conflict: file was modified externally', currentEtag }, 409);
            }
        }
        writeFileSync(resolved, newContent, 'utf-8');
        const newEtag = computeEtag(newContent);
        const stat = statSync(resolved);
        c.header('ETag', `"${newEtag}"`);
        return c.json({ ok: true, path: filePath, size: stat.size });
    });
    return app;
}
//# sourceMappingURL=artifacts.js.map