"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectProjectDNA = detectProjectDNA;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// ─── Detection ────────────────────────────────────────────────────────────
function readJson(file) {
    try {
        return JSON.parse(fs.readFileSync(file, 'utf-8'));
    }
    catch {
        return null;
    }
}
function fileExists(root, ...parts) {
    return fs.existsSync(path.join(root, ...parts));
}
function readFile(root, ...parts) {
    try {
        return fs.readFileSync(path.join(root, ...parts), 'utf-8');
    }
    catch {
        return '';
    }
}
function countFilesInDir(root, subdir) {
    const dir = path.join(root, subdir);
    if (!fs.existsSync(dir))
        return 0;
    let count = 0;
    const walk = (d) => {
        const entries = fs.readdirSync(d, { withFileTypes: true });
        for (const e of entries) {
            if (e.isFile())
                count++;
            else if (e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules') {
                walk(path.join(d, e.name));
            }
        }
    };
    try {
        walk(dir);
    }
    catch { }
    return count;
}
function hasFolder(root, name) {
    return fs.existsSync(path.join(root, name)) && fs.statSync(path.join(root, name)).isDirectory();
}
function getTopFolders(root) {
    try {
        return fs.readdirSync(root, { withFileTypes: true })
            .filter(d => d.isDirectory() && !d.name.startsWith('.') && d.name !== 'node_modules')
            .map(d => d.name);
    }
    catch {
        return [];
    }
}
// ─── Deep Stack Detection ─────────────────────────────────────────────────
function detectFromPackageJson(root, pkg) {
    const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
    const depNames = Object.keys(deps);
    const has = (name) => depNames.some(d => d === name || d.startsWith(name + '@'));
    const hasAny = (...names) => names.some(n => has(n));
    const findVersion = (name) => deps[name] || deps[`@types/${name}`] || '';
    const result = {};
    // Frontend
    if (has('next'))
        result.frontend = 'Next.js';
    else if (has('react'))
        result.frontend = has('react-native') ? 'React Native' : 'React';
    else if (has('vue'))
        result.frontend = 'Vue';
    else if (has('@angular/core'))
        result.frontend = 'Angular';
    else if (has('svelte'))
        result.frontend = 'Svelte';
    else if (has('express'))
        result.frontend = 'none';
    // Backend
    if (has('express'))
        result.backend = 'Express';
    else if (has('fastify'))
        result.backend = 'Fastify';
    else if (has('@nestjs/core'))
        result.backend = 'NestJS';
    else if (has('koa'))
        result.backend = 'Koa';
    else if (has('next'))
        result.backend = 'Next.js API Routes';
    else if (has('django'))
        result.backend = 'Django';
    else if (has('flask'))
        result.backend = 'Flask';
    else
        result.backend = 'none';
    // Database
    const dbs = [];
    if (has('pg') || has('postgres'))
        dbs.push('PostgreSQL');
    if (has('mysql') || has('mysql2'))
        dbs.push('MySQL');
    if (has('mongodb') || has('mongoose'))
        dbs.push('MongoDB');
    if (has('redis') || has('ioredis'))
        dbs.push('Redis');
    if (has('better-sqlite3') || has('sqlite3'))
        dbs.push('SQLite');
    result.database = dbs.length > 0 ? dbs : ['none'];
    // ORM
    if (has('prisma') || has('@prisma/client'))
        result.orm = 'Prisma';
    else if (has('typeorm'))
        result.orm = 'TypeORM';
    else if (has('drizzle-orm'))
        result.orm = 'Drizzle';
    else if (has('sequelize'))
        result.orm = 'Sequelize';
    else if (has('mongoose'))
        result.orm = 'Mongoose';
    else if (has('knex'))
        result.orm = 'Knex';
    else
        result.orm = 'none';
    // Validation
    if (has('zod'))
        result.validation = 'Zod';
    else if (has('yup'))
        result.validation = 'Yup';
    else if (has('joi'))
        result.validation = 'Joi';
    else if (has('class-validator'))
        result.validation = 'class-validator';
    else
        result.validation = 'none';
    // Testing
    const tests = [];
    if (has('jest') || has('ts-jest') || has('@jest/globals'))
        tests.push('Jest');
    if (has('vitest'))
        tests.push('Vitest');
    if (has('playwright') || has('@playwright/test'))
        tests.push('Playwright');
    if (has('cypress'))
        tests.push('Cypress');
    if (has('mocha'))
        tests.push('Mocha');
    result.testing = tests.length > 0 ? tests : ['none'];
    // State Management
    if (has('zustand'))
        result.stateManagement = 'Zustand';
    else if (has('@reduxjs/toolkit') || has('redux'))
        result.stateManagement = 'Redux';
    else if (has('jotai'))
        result.stateManagement = 'Jotai';
    else if (has('recoil'))
        result.stateManagement = 'Recoil';
    else if (has('mobx'))
        result.stateManagement = 'MobX';
    else
        result.stateManagement = 'none';
    // Styling
    if (fileExists(root, 'tailwind.config.js') || fileExists(root, 'tailwind.config.ts') || has('tailwindcss'))
        result.styling = 'Tailwind CSS';
    else if (has('styled-components'))
        result.styling = 'Styled Components';
    else if (has('@emotion/react'))
        result.styling = 'Emotion';
    else if (has('sass'))
        result.styling = 'Sass';
    else
        result.styling = 'none';
    // Auth
    if (has('next-auth'))
        result.auth = 'NextAuth';
    else if (has('@clerk/nextjs'))
        result.auth = 'Clerk';
    else if (has('passport'))
        result.auth = 'Passport';
    else if (has('jsonwebtoken') && !has('next-auth'))
        result.auth = 'JWT';
    else
        result.auth = 'none';
    // API Style
    if (fileExists(root, 'app/api'))
        result.apiStyle = 'REST (Next.js Route Handlers)';
    else if (has('@trpc/server'))
        result.apiStyle = 'tRPC';
    else if (has('graphql') || has('@apollo/server'))
        result.apiStyle = 'GraphQL';
    else if (has('express'))
        result.apiStyle = 'REST (Express)';
    else
        result.apiStyle = 'none';
    // Monorepo
    if (has('turbo') || has('@turbo/gen'))
        result.monorepo = 'Turborepo';
    else if (has('nx') || has('@nrwl/workspace'))
        result.monorepo = 'Nx';
    else if (fileExists(root, 'pnpm-workspace.yaml'))
        result.monorepo = 'pnpm Workspaces';
    else if (has('lerna'))
        result.monorepo = 'Lerna';
    else
        result.monorepo = 'none';
    // Package Manager
    if (fileExists(root, 'pnpm-lock.yaml'))
        result.packageManager = 'pnpm';
    else if (fileExists(root, 'yarn.lock'))
        result.packageManager = 'yarn';
    else if (fileExists(root, 'bun.lockb'))
        result.packageManager = 'bun';
    else
        result.packageManager = 'npm';
    // Deployment
    const deploys = [];
    if (fileExists(root, 'vercel.json') || has('vercel'))
        deploys.push('Vercel');
    if (fileExists(root, 'Dockerfile') || fileExists(root, 'docker-compose.yml'))
        deploys.push('Docker');
    if (fileExists(root, '.github/workflows'))
        deploys.push('GitHub Actions');
    if (fileExists(root, 'railway.json') || has('@railway/cli'))
        deploys.push('Railway');
    if (fileExists(root, 'fly.toml'))
        deploys.push('Fly.io');
    result.deployment = deploys.length > 0 ? deploys : ['unknown'];
    // Languages
    const langs = [];
    if (has('typescript') || has('@types/'))
        langs.push('TypeScript');
    if (depNames.some(d => !d.startsWith('@types/') && !d.startsWith('@')))
        langs.push('JavaScript');
    const extCounts = {};
    const srcDir = path.join(root, 'src');
    if (fs.existsSync(srcDir))
        countExtensions(srcDir, extCounts);
    else
        countExtensions(root, extCounts);
    const extKeys = Object.keys(extCounts);
    if (extKeys.includes('.py'))
        langs.push('Python');
    if (extKeys.includes('.go'))
        langs.push('Go');
    if (extKeys.includes('.rs'))
        langs.push('Rust');
    if (extKeys.includes('.java'))
        langs.push('Java');
    result.languages = langs.length > 0 ? langs : ['JavaScript'];
    return result;
}
function countExtensions(dir, counts) {
    try {
        const entries = fs.readdirSync(dir, { withFileTypes: true });
        for (const e of entries) {
            if (e.name.startsWith('.') || e.name === 'node_modules')
                continue;
            const full = path.join(dir, e.name);
            if (e.isDirectory())
                countExtensions(full, counts);
            else {
                const ext = path.extname(e.name).toLowerCase();
                if (ext)
                    counts[ext] = (counts[ext] || 0) + 1;
            }
        }
    }
    catch { }
}
// ─── Architecture Detection ────────────────────────────────────────────────
function detectArchitecture(root) {
    const folders = getTopFolders(root);
    const srcDirs = ['src', 'app', 'lib', 'pages', 'components', 'services', 'server'];
    let type = 'flat';
    let confidence = 'low';
    const coreModules = [];
    // Check for feature-based
    const hasFeatures = folders.some(d => {
        const sub = getTopFolders(path.join(root, d));
        return sub.length >= 2 && sub.some(s => s === 'components' || s === 'services' || s === 'hooks');
    });
    const hasSrc = folders.includes('src');
    const hasApp = folders.includes('app');
    // Feature-based check
    if (hasSrc) {
        const srcFolders = getTopFolders(path.join(root, 'src'));
        const featureLike = srcFolders.filter(f => f !== 'components' && f !== 'utils' && f !== 'types' && f !== 'lib' && f !== 'styles' && f !== 'assets');
        if (featureLike.length >= 3 && srcFolders.length >= 3) {
            type = 'feature-based';
            confidence = 'high';
        }
    }
    // Next.js App Router
    if (hasApp && fileExists(root, 'app/layout.tsx')) {
        type = 'Next.js App Router';
        confidence = 'high';
        coreModules.push('app/');
    }
    // Layer-based
    const layerDirs = ['controllers', 'services', 'models', 'routes', 'middleware', 'repositories'];
    const hasLayers = folders.some(d => layerDirs.includes(d));
    if (hasLayers || (hasSrc && getTopFolders(path.join(root, 'src')).some(d => layerDirs.includes(d)))) {
        type = 'layer-based';
        confidence = 'high';
        coreModules.push(...layerDirs.filter(d => folders.includes(d)));
    }
    // Microservices
    const hasServicesDir = folders.includes('services') || folders.includes('packages');
    if (hasServicesDir) {
        const svcDir = path.join(root, folders.includes('services') ? 'services' : 'packages');
        const subDirs = getTopFolders(svcDir);
        if (subDirs.length >= 3) {
            type = 'microservices';
            confidence = 'medium';
        }
    }
    // Monolith detection
    if (type === 'flat' && folders.length >= 10) {
        type = 'monolith';
        confidence = 'low';
    }
    // Entry points
    const entryPoints = [];
    if (fileExists(root, 'src/index.ts'))
        entryPoints.push('src/index.ts');
    if (fileExists(root, 'app/layout.tsx'))
        entryPoints.push('app/layout.tsx');
    if (fileExists(root, 'pages/index.tsx'))
        entryPoints.push('pages/index.tsx');
    if (fileExists(root, 'index.js'))
        entryPoints.push('index.js');
    if (fileExists(root, 'src/main.ts'))
        entryPoints.push('src/main.ts');
    if (fileExists(root, 'src/server.ts'))
        entryPoints.push('src/server.ts');
    // Core modules
    const potentialCore = ['payment', 'auth', 'users', 'orders', 'products', 'api', 'dashboard'];
    for (const mod of potentialCore) {
        if (hasFolder(root, mod) || hasFolder(root, path.join('src', mod)) || hasFolder(root, path.join('app', mod))) {
            coreModules.push(mod);
        }
    }
    return {
        type,
        confidence,
        entryPoints: entryPoints.length > 0 ? entryPoints : ['unknown'],
        topLevelDirs: folders.slice(0, 10),
        coreModules: coreModules.length > 0 ? coreModules : ['unknown'],
    };
}
// ─── Purpose Detection ─────────────────────────────────────────────────────
function detectPurpose(root) {
    // Try README first
    const readme = readFile(root, 'README.md') || readFile(root, 'readme.md');
    if (readme) {
        const lines = readme.split('\n');
        const title = lines.find(l => l.startsWith('# '));
        if (title)
            return title.replace(/^#\s+/, '').trim();
    }
    // Try package.json description
    const pkg = readJson(path.join(root, 'package.json'));
    if (pkg?.description)
        return pkg.description;
    // Try key folder names
    const folders = getTopFolders(root);
    const bizFolders = folders.filter(f => !['src', 'app', 'lib', 'node_modules', 'components', 'utils', 'types', 'styles', 'public', 'dist', 'build'].includes(f));
    if (bizFolders.length > 0)
        return `Project with: ${bizFolders.join(', ')}`;
    return 'unknown';
}
// ─── Public API ────────────────────────────────────────────────────────────
function detectProjectDNA(root) {
    const pkg = readJson(path.join(root, 'package.json'));
    const name = pkg?.name || path.basename(root);
    let stack = {
        frontend: 'none', backend: 'none', database: ['none'], orm: 'none',
        validation: 'none', testing: ['none'], stateManagement: 'none', styling: 'none',
        auth: 'none', apiStyle: 'none', monorepo: 'none', packageManager: 'npm',
        deployment: ['unknown'], languages: ['JavaScript'], trustScore: 50,
    };
    if (pkg) {
        const detected = detectFromPackageJson(root, pkg);
        stack = { ...stack, ...detected };
        // Trust score calculation
        let score = 0;
        if (stack.frontend !== 'none')
            score += 15;
        if (stack.backend !== 'none')
            score += 10;
        if (stack.database[0] !== 'none')
            score += 10;
        if (stack.orm !== 'none')
            score += 10;
        if (stack.validation !== 'none')
            score += 10;
        if (stack.testing[0] !== 'none')
            score += 10;
        if (stack.auth !== 'none')
            score += 10;
        if (stack.apiStyle !== 'none')
            score += 10;
        if (stack.monorepo !== 'none')
            score += 5;
        if (stack.deployment[0] !== 'unknown')
            score += 10;
        stack.trustScore = Math.min(100, score);
    }
    const architecture = detectArchitecture(root);
    const purpose = detectPurpose(root);
    // Count files and lines
    let fileCount = 0;
    let lineCount = 0;
    const countDir = (d) => {
        try {
            const entries = fs.readdirSync(d, { withFileTypes: true });
            for (const e of entries) {
                if (e.name.startsWith('.') || e.name === 'node_modules' || e.name === 'dist' || e.name === '.git')
                    continue;
                const full = path.join(d, e.name);
                if (e.isDirectory())
                    countDir(full);
                else {
                    fileCount++;
                    try {
                        lineCount += fs.readFileSync(full, 'utf-8').split('\n').length;
                    }
                    catch { }
                }
            }
        }
        catch { }
    };
    countDir(root);
    return { stack, architecture, fileCount, lineCount, name, purpose };
}
//# sourceMappingURL=stack-detector.js.map