/*
 * 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 { closeSync, existsSync, openSync, readdirSync, readFileSync, readSync, statSync } from 'node:fs';
import { extname, join, relative, resolve } from 'node:path';
import { Hono } from 'hono';
import { getWorkspaceCwd } from '../middleware/workspace.js';
const IGNORED_DIRS = new Set([
    'node_modules',
    '.git',
    '.hg',
    '.svn',
    'dist',
    'build',
    'out',
    'lib',
    '.next',
    '.nuxt',
    'coverage',
    '__pycache__',
    '.tsbuildinfo',
    '.claude',
    'schemas',
    'fixtures',
    'test-fixtures',
]);
const MAX_DEPTH = 6;
const MAX_FILES = 500;
function scanDir(dir, baseDir, depth, results, exts) {
    if (depth > MAX_DEPTH || results.length >= MAX_FILES)
        return;
    if (!existsSync(dir))
        return;
    let entries;
    try {
        entries = readdirSync(dir, { withFileTypes: true });
    }
    catch {
        return;
    }
    for (const entry of entries) {
        if (results.length >= MAX_FILES)
            return;
        if (entry.isDirectory()) {
            if (IGNORED_DIRS.has(entry.name))
                continue;
            if (depth === 0 && entry.name.startsWith('.'))
                continue;
            scanDir(join(dir, entry.name), baseDir, depth + 1, results, exts);
        }
        else if (entry.isFile()) {
            const ext = extname(entry.name).toLowerCase();
            if (exts && !exts.has(ext))
                continue;
            try {
                const fullPath = join(dir, entry.name);
                const stat = statSync(fullPath);
                results.push({
                    path: relative(baseDir, fullPath),
                    name: entry.name,
                    ext: ext.slice(1),
                    size: stat.size,
                    modified: stat.mtime.toISOString(),
                });
            }
            catch {
                // skip unreadable files
            }
        }
    }
}
function isDiagramJson(filePath, fileSize) {
    try {
        const readSize = Math.min(fileSize, 8192);
        const fd = openSync(filePath, 'r');
        const buf = Buffer.alloc(readSize);
        readSync(fd, buf, 0, readSize, 0);
        closeSync(fd);
        const raw = buf.toString('utf-8');
        if (raw.includes('"backbones"'))
            return true;
        if (raw.includes('"pages"') && raw.includes('"items"'))
            return true;
        if (raw.includes('"nodes"') && raw.includes('"edges"'))
            return true;
        if (raw.includes('"mxGraphModel"') || raw.includes('"mxfile"'))
            return true;
        return false;
    }
    catch {
        return false;
    }
}
export function filesRoute(cwd) {
    const app = new Hono();
    app.get('/scan', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const extParam = c.req.query('ext');
        const exts = extParam ? new Set(extParam.split(',').map((e) => (e.startsWith('.') ? e : `.${e}`))) : undefined;
        const results = [];
        scanDir(dir, dir, 0, results, exts);
        return c.json({ files: results, total: results.length, truncated: results.length >= MAX_FILES });
    });
    app.get('/diagrams', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const diagramExts = new Set(['.drawio', '.svg']);
        const allExts = new Set(['.drawio', '.svg', '.json']);
        const results = [];
        scanDir(dir, dir, 0, results, allExts);
        const diagrams = results.filter((f) => {
            if (diagramExts.has(`.${f.ext}`))
                return true;
            if (f.ext === 'json')
                return isDiagramJson(join(dir, f.path), f.size);
            return false;
        });
        return c.json({ files: diagrams, total: diagrams.length });
    });
    app.get('/storymaps', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const jsonExts = new Set(['.json']);
        const results = [];
        scanDir(dir, dir, 0, results, jsonExts);
        const storymaps = results.filter((f) => {
            const filePath = join(dir, f.path);
            try {
                const readSize = Math.min(f.size, 8192);
                const fd = openSync(filePath, 'r');
                const buf = Buffer.alloc(readSize);
                readSync(fd, buf, 0, readSize, 0);
                closeSync(fd);
                const raw = buf.toString('utf-8');
                if (raw.includes('"backbones"'))
                    return true;
                if (raw.includes('"pages"') && raw.includes('"items"'))
                    return true;
                if (raw.includes('"epics"') && raw.includes('"stories"'))
                    return true;
                return false;
            }
            catch {
                return false;
            }
        });
        return c.json({ files: storymaps, total: storymaps.length });
    });
    app.get('/read/:path{.+}', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const filePath = c.req.param('path');
        if (!filePath)
            return c.json({ error: 'No file path provided' }, 400);
        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);
        try {
            const stat = statSync(resolved);
            if (!stat.isFile())
                return c.json({ error: 'Not a file' }, 400);
            if (stat.size > 5 * 1024 * 1024)
                return c.json({ error: 'File too large (>5MB)' }, 413);
            const content = readFileSync(resolved, 'utf-8');
            const ext = extname(filePath).toLowerCase().slice(1);
            return c.json({ content, ext, size: stat.size });
        }
        catch {
            return c.json({ error: 'Failed to read file' }, 500);
        }
    });
    return app;
}
//# sourceMappingURL=files.js.map