import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
import { detectAiTools, getPrimaryAiTool } from '../ai-tool-detector/index.js';
import { assertEnglishOnlyArtifactText } from '../spec-language/english-only.js';
// Stack-specific architecture rules catalog
export const ARCHITECTURE_RULES = {
    nextjs: {
        title: 'Next.js API Architecture',
        description: 'Clean separation for Next.js API routes',
        layers: [
            {
                name: 'Route Handler',
                path: 'app/api/',
                responsibility: 'Parse request, validate input, call service, format response',
            },
            {
                name: 'Service',
                path: 'lib/services/',
                responsibility: 'Business logic, orchestration, transaction boundaries',
            },
            {
                name: 'Repository',
                path: 'lib/repositories/',
                responsibility: 'Data access (DB queries, external APIs)',
            },
        ],
        conventions: [
            'Never put business logic in route handlers',
            'Route handlers should be under 30 lines',
            'Services are framework-agnostic (no NextRequest/NextResponse)',
            'Repositories return plain objects, never ORM entities',
        ],
        maxLinesPerHandler: 30,
    },
    express: {
        title: 'Express Architecture',
        description: 'Clean separation for Express.js APIs',
        layers: [
            {
                name: 'Controller',
                path: 'src/controllers/',
                responsibility: 'Parse request, validate, respond',
            },
            {
                name: 'Service',
                path: 'src/services/',
                responsibility: 'Business logic and orchestration',
            },
            {
                name: 'Repository',
                path: 'src/repositories/',
                responsibility: 'Data access layer',
            },
        ],
        conventions: [
            'Controllers call services, never repositories directly',
            'Services are stateless',
            'One file per resource',
        ],
        maxLinesPerHandler: 40,
    },
    fastapi: {
        title: 'FastAPI Architecture',
        description: 'Clean separation for Python FastAPI',
        layers: [
            {
                name: 'Router',
                path: 'app/routers/',
                responsibility: 'Endpoint definitions, input validation',
            },
            {
                name: 'Service',
                path: 'app/services/',
                responsibility: 'Business logic',
            },
            {
                name: 'Repository',
                path: 'app/repositories/',
                responsibility: 'Database access (SQLAlchemy/Prisma)',
            },
        ],
        conventions: [
            'Use dependency injection via Depends()',
            'Pydantic models for all I/O',
            'Async by default',
        ],
        maxLinesPerHandler: 30,
    },
    default: {
        title: 'Clean Architecture',
        description: 'Generic clean architecture rules',
        layers: [
            {
                name: 'Handler',
                path: 'src/handlers/',
                responsibility: 'Input/output boundary',
            },
            {
                name: 'Service',
                path: 'src/services/',
                responsibility: 'Business logic',
            },
            {
                name: 'Repository',
                path: 'src/repositories/',
                responsibility: 'Data access',
            },
        ],
        conventions: [
            'Separate concerns by layer',
            'Dependencies point inward',
            'No framework code in business logic',
        ],
        maxLinesPerHandler: 50,
    },
};
/** Detect stack from project files. Returns a key into ARCHITECTURE_RULES. */
export function detectArchitectureStack(projectPath) {
    // Next.js detection
    if (existsSync(join(projectPath, 'next.config.js')) ||
        existsSync(join(projectPath, 'next.config.ts')) ||
        existsSync(join(projectPath, 'next.config.mjs'))) {
        return 'nextjs';
    }
    // FastAPI detection
    const requirementsTxt = join(projectPath, 'requirements.txt');
    if (existsSync(requirementsTxt)) {
        try {
            const content = readFileSync(requirementsTxt, 'utf-8');
            if (content.toLowerCase().includes('fastapi')) {
                return 'fastapi';
            }
        }
        catch {
            // best-effort
        }
    }
    // Express detection via package.json
    const packageJsonPath = join(projectPath, 'package.json');
    if (existsSync(packageJsonPath)) {
        try {
            const raw = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
            if (typeof raw === 'object' && raw !== null) {
                const pkg = raw;
                const deps = {
                    ...(typeof pkg.dependencies === 'object'
                        ? pkg.dependencies
                        : {}),
                    ...(typeof pkg.devDependencies === 'object'
                        ? pkg.devDependencies
                        : {}),
                };
                if ('express' in deps) {
                    return 'express';
                }
            }
        }
        catch {
            // best-effort
        }
    }
    return 'default';
}
/** Format layers table as markdown. */
function formatLayersTable(layers) {
    const rows = layers.map((l) => `| ${l.name} | \`${l.path}\` | ${l.responsibility} |`);
    return ['| Layer | Path | Responsibility |', '|-------|------|----------------|', ...rows].join('\n');
}
/** Generate rules content formatted for a specific AI tool. */
export function generateRulesContent(rule, tool) {
    const conventions = rule.conventions.map((c) => `- ${c}`).join('\n');
    const layersTable = formatLayersTable(rule.layers);
    // All tools use markdown; formatting varies slightly per tool
    switch (tool.toolId) {
        case 'cursor':
        case 'windsurf': {
            // Flat markdown without deep nesting — cursor/windsurf prefer concise rules
            return [
                `# ${rule.title}`,
                '',
                rule.description,
                '',
                '## Layers',
                layersTable,
                '',
                '## Conventions',
                conventions,
                '',
                `## Handler Size Limit`,
                `Max ${rule.maxLinesPerHandler} lines per handler/controller.`,
                '',
            ].join('\n');
        }
        case 'copilot': {
            // GitHub Copilot instructions format
            return [
                `# Architecture Rules: ${rule.title}`,
                '',
                `> ${rule.description}`,
                '',
                '## Layer Structure',
                layersTable,
                '',
                '## Coding Conventions',
                conventions,
                '',
                `## Size Constraints`,
                `- Max ${rule.maxLinesPerHandler} lines per handler`,
                '',
            ].join('\n');
        }
        case 'aider': {
            // Aider CONVENTIONS.md style
            return [
                `# ${rule.title}`,
                '',
                rule.description,
                '',
                '## Architecture Layers',
                layersTable,
                '',
                '## Conventions',
                conventions,
                '',
                `## Constraints`,
                `- Handlers/controllers: max ${rule.maxLinesPerHandler} lines`,
                '',
            ].join('\n');
        }
        case 'claude':
        default: {
            // Standard markdown with full headers — Claude Code format
            return [
                `# ${rule.title}`,
                '',
                `> ${rule.description}`,
                '',
                '## Layer Separation (MANDATORY)',
                '',
                layersTable,
                '',
                '## Conventions (MANDATORY)',
                '',
                conventions,
                '',
                '## Size Budget',
                '',
                `- Max **${rule.maxLinesPerHandler} lines** per handler/controller`,
                '- Extract helpers when exceeding this limit',
                '',
            ].join('\n');
        }
    }
}
/** Resolve the file path where rules should be written for a given tool. */
function resolveRulesFilePath(projectPath, tool, stackKey) {
    switch (tool.toolId) {
        case 'claude':
            return join(projectPath, tool.rulesPath, `architecture-${stackKey}.md`);
        case 'cursor':
            return join(projectPath, '.cursorrules');
        case 'copilot':
            return join(projectPath, '.github', 'copilot-instructions.md');
        case 'windsurf':
            return join(projectPath, '.windsurfrules');
        case 'aider':
            return join(projectPath, 'CONVENTIONS.md');
        default:
            return join(projectPath, tool.rulesPath, `architecture-${stackKey}.md`);
    }
}
/** Main entry: detect stack + tools, generate all rules objects. */
export function generateProjectRules(projectPath) {
    const stackKey = detectArchitectureStack(projectPath);
    const defaultRule = ARCHITECTURE_RULES.default;
    const rule = ARCHITECTURE_RULES[stackKey] ?? defaultRule;
    if (!rule) {
        return [];
    }
    const tools = detectAiTools(projectPath);
    // If no tools detected, generate for claude (default)
    const effectiveTools = tools.length > 0 ? tools : [getPrimaryAiTool(projectPath)];
    return effectiveTools.map((tool) => {
        const filePath = resolveRulesFilePath(projectPath, tool, stackKey);
        const alreadyExists = existsSync(filePath);
        const content = generateRulesContent(rule, tool);
        return { tool, content, filePath, alreadyExists };
    });
}
/** Write rules to disk, skipping files that already exist. */
export function writeRules(rules) {
    const written = [];
    const skipped = [];
    for (const rule of rules) {
        if (rule.alreadyExists) {
            skipped.push(rule.filePath);
            continue;
        }
        try {
            const dir = rule.filePath.substring(0, rule.filePath.lastIndexOf('/'));
            if (dir) {
                mkdirSync(dir, { recursive: true });
            }
            assertEnglishOnlyArtifactText(rule.content, 'rule');
            writeFileSync(rule.filePath, rule.content, 'utf-8');
            written.push(rule.filePath);
        }
        catch {
            // best-effort — skip on error
            skipped.push(rule.filePath);
        }
    }
    return { written, skipped };
}
//# sourceMappingURL=index.js.map