"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.detectToolingHints = detectToolingHints;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const ignoredDirectoryNames = new Set([
    '.git',
    '.archpilot',
    '.archpilot_',
    '.vscode-test',
    'tests',
    'test',
    '__tests__',
    'fixtures',
    'smoke',
    'docs',
    'doc',
    'reports',
    'report',
    'badges',
    'examples',
    'node_modules',
    'dist',
    'build',
    'out',
    'coverage',
    '.next',
    'target',
    'vendor',
    '.venv',
    'venv',
    '__pycache__',
]);
function normalizePath(value) {
    return value.replaceAll('\\', '/');
}
function uniqueSorted(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function addEvidence(evidence, value) {
    if (!evidence.includes(value)) {
        evidence.push(value);
    }
}
function isIgnoredPath(ignoreMatcher, relativePath) {
    return ignoreMatcher?.isIgnored(normalizePath(relativePath)) ?? false;
}
function safeReadDirEntries(directoryPath) {
    try {
        return fs.readdirSync(directoryPath, { withFileTypes: true });
    }
    catch {
        return [];
    }
}
function safeReadText(filePath, maxChars = 80_000) {
    try {
        const content = fs.readFileSync(filePath, { encoding: 'utf8' });
        return content.slice(0, maxChars);
    }
    catch {
        return undefined;
    }
}
function scanWorkspace(workspaceRoot, maxDepth = 7, ignoreMatcher) {
    const files = [];
    const directories = [];
    const queue = [
        { abs: workspaceRoot, rel: '', depth: 0 },
    ];
    while (queue.length > 0) {
        const current = queue.shift();
        if (!current) {
            continue;
        }
        const entries = safeReadDirEntries(current.abs)
            .sort((left, right) => left.name.localeCompare(right.name));
        for (const entry of entries) {
            const absPath = path.join(current.abs, entry.name);
            const relPath = current.rel.length > 0 ? `${current.rel}/${entry.name}` : entry.name;
            if (isIgnoredPath(ignoreMatcher, relPath)) {
                continue;
            }
            if (entry.isDirectory()) {
                if (current.depth >= maxDepth || ignoredDirectoryNames.has(entry.name)) {
                    continue;
                }
                directories.push(normalizePath(relPath));
                queue.push({ abs: absPath, rel: relPath, depth: current.depth + 1 });
                continue;
            }
            if (entry.isFile()) {
                files.push(normalizePath(relPath));
            }
        }
    }
    return {
        files: uniqueSorted(files),
        directories: uniqueSorted(directories),
    };
}
function parsePackageJsonSummaries(workspaceRoot, files) {
    const packageJsonPaths = files.filter((entry) => entry === 'package.json' || entry.endsWith('/package.json'));
    const summaries = [];
    for (const packageJsonPath of packageJsonPaths) {
        const absolutePath = path.join(workspaceRoot, ...packageJsonPath.split('/'));
        const contents = safeReadText(absolutePath);
        if (!contents) {
            continue;
        }
        try {
            const parsed = JSON.parse(contents);
            const dependencies = new Set();
            for (const fieldName of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) {
                const value = parsed[fieldName];
                if (!value || typeof value !== 'object' || Array.isArray(value)) {
                    continue;
                }
                for (const name of Object.keys(value)) {
                    dependencies.add(name);
                }
            }
            const scriptsValue = parsed.scripts;
            const scripts = scriptsValue && typeof scriptsValue === 'object' && !Array.isArray(scriptsValue)
                ? Object.fromEntries(Object.entries(scriptsValue)
                    .filter((entry) => typeof entry[1] === 'string'))
                : {};
            summaries.push({
                path: packageJsonPath,
                dependencies,
                scripts,
                ...(typeof parsed.packageManager === 'string' ? { packageManager: parsed.packageManager } : {}),
            });
        }
        catch {
            // Ignore invalid package.json files for detection.
        }
    }
    return summaries;
}
function parseComposerJsonSummaries(workspaceRoot, files) {
    const composerJsonPaths = files.filter((entry) => entry === 'composer.json' || entry.endsWith('/composer.json'));
    const summaries = [];
    for (const composerJsonPath of composerJsonPaths) {
        const absolutePath = path.join(workspaceRoot, ...composerJsonPath.split('/'));
        const contents = safeReadText(absolutePath);
        if (!contents) {
            continue;
        }
        try {
            const parsed = JSON.parse(contents);
            const dependencies = new Set();
            for (const fieldName of ['require', 'require-dev']) {
                const value = parsed[fieldName];
                if (!value || typeof value !== 'object' || Array.isArray(value)) {
                    continue;
                }
                for (const name of Object.keys(value)) {
                    dependencies.add(name.toLowerCase());
                }
            }
            summaries.push({ path: composerJsonPath, dependencies });
        }
        catch {
            // Ignore invalid composer.json files for detection.
        }
    }
    return summaries;
}
function dependencyExists(packageJsonSummaries, name) {
    return packageJsonSummaries.some((summary) => summary.dependencies.has(name));
}
function composerDependencyExists(composerJsonSummaries, name) {
    const normalized = name.toLowerCase();
    return composerJsonSummaries.some((summary) => summary.dependencies.has(normalized));
}
function fileExists(files, name) {
    return files.some((entry) => entry === name || entry.endsWith(`/${name}`));
}
function anyFileExists(files, names) {
    return names.some((name) => fileExists(files, name));
}
function anyPackageManager(packageJsonSummaries, pattern) {
    return packageJsonSummaries.some((summary) => summary.packageManager ? pattern.test(summary.packageManager) : false);
}
function configTextMatches(workspaceRoot, files, filePattern, textPattern) {
    return files
        .filter((entry) => filePattern.test(entry))
        .slice(0, 80)
        .some((entry) => {
        const content = safeReadText(path.join(workspaceRoot, ...entry.split('/')), 60_000);
        return content ? textPattern.test(content) : false;
    });
}
function detectToolingHints(workspaceRoot, options) {
    const scan = scanWorkspace(workspaceRoot, 7, options?.ignoreMatcher);
    const packageJsonSummaries = parsePackageJsonSummaries(workspaceRoot, scan.files);
    const composerJsonSummaries = parseComposerJsonSummaries(workspaceRoot, scan.files);
    const toolingHints = [];
    const databaseHints = [];
    const evidence = [];
    let score = 0;
    const addToolingHint = (hint, note, points) => {
        if (!toolingHints.includes(hint)) {
            toolingHints.push(hint);
        }
        addEvidence(evidence, note);
        score += points;
    };
    const addDatabaseHint = (hint, note) => {
        if (!databaseHints.includes(hint)) {
            databaseHints.push(hint);
        }
        addEvidence(evidence, note);
    };
    if (scan.files.includes('pnpm-workspace.yaml')) {
        addToolingHint('pnpm-workspaces', 'Found pnpm-workspace.yaml', 2);
    }
    if (anyFileExists(scan.files, ['bun.lock', 'bun.lockb']) || anyPackageManager(packageJsonSummaries, /^bun@/iu)) {
        addToolingHint('bun', 'Found Bun lockfile or packageManager hint', 3);
    }
    if (anyFileExists(scan.files, ['deno.json', 'deno.jsonc', 'deno.lock'])) {
        addToolingHint('deno', 'Found Deno config or lockfile', 3);
    }
    if (scan.files.some((entry) => /(^|\/)vite\.config\.(?:js|mjs|cjs|ts|mts|cts)$/iu.test(entry)) ||
        dependencyExists(packageJsonSummaries, 'vite')) {
        addToolingHint('vite', 'Found Vite config or dependency', 2);
    }
    const prismaSchemaPath = scan.files.find((entry) => entry === 'prisma/schema.prisma' || entry.endsWith('/prisma/schema.prisma'));
    const hasPrismaDependency = packageJsonSummaries.some((summary) => summary.dependencies.has('prisma'));
    const hasPrismaClientDependency = packageJsonSummaries.some((summary) => summary.dependencies.has('@prisma/client'));
    const hasPrismaScriptUsage = packageJsonSummaries.some((summary) => Object.values(summary.scripts).some((script) => /\bprisma\s+(migrate|generate)\b/iu.test(script)));
    const hasPrismaEvidence = Boolean(prismaSchemaPath) ||
        hasPrismaDependency ||
        hasPrismaClientDependency ||
        hasPrismaScriptUsage;
    if (prismaSchemaPath) {
        addToolingHint('prisma', `Found ${prismaSchemaPath}`, 4);
        const schemaText = safeReadText(path.join(workspaceRoot, ...prismaSchemaPath.split('/')));
        if (schemaText) {
            for (const provider of ['postgresql', 'mysql', 'mariadb', 'mongodb', 'sqlite', 'sqlserver']) {
                if (new RegExp(`provider\\s*=\\s*["']${provider}["']`, 'iu').test(schemaText)) {
                    addDatabaseHint(provider, `Found Prisma datasource provider ${provider}`);
                }
            }
        }
    }
    if (hasPrismaDependency) {
        addToolingHint('prisma', 'Found package.json with prisma dependency', 2);
    }
    if (hasPrismaClientDependency) {
        addToolingHint('prisma', 'Found package.json with @prisma/client dependency', 2);
    }
    if (hasPrismaScriptUsage) {
        addToolingHint('prisma', 'Found package.json scripts using prisma migrate/generate', 1);
    }
    const ormPackageHints = [
        { hint: 'drizzle-orm', dependencies: ['drizzle-orm'], note: 'Found package.json with drizzle-orm dependency' },
        { hint: 'typeorm', dependencies: ['typeorm'], note: 'Found package.json with typeorm dependency' },
        { hint: 'sequelize', dependencies: ['sequelize', '@sequelize/core'], note: 'Found package.json with Sequelize dependency' },
        { hint: 'mikro-orm', dependencies: ['@mikro-orm/core', 'mikro-orm'], note: 'Found package.json with MikroORM dependency' },
    ];
    for (const ormHint of ormPackageHints) {
        if (ormHint.dependencies.some((dependency) => dependencyExists(packageJsonSummaries, dependency))) {
            addToolingHint(ormHint.hint, ormHint.note, 2);
        }
    }
    if (configTextMatches(workspaceRoot, scan.files, /(^|\/)(pom\.xml|build\.gradle(\.kts)?|[^/]+\.java|[^/]+\.kt)$/iu, /\b(org\.hibernate|hibernate-core|jakarta\.persistence|javax\.persistence|@Entity\b|@PersistenceContext\b)/u)) {
        addToolingHint('hibernate', 'Found Hibernate/JPA dependency or entity evidence', 2);
    }
    if (configTextMatches(workspaceRoot, scan.files, /(^|\/)(pom\.xml|build\.gradle(\.kts)?|[^/]+\.java|[^/]+\.kt)$/iu, /\b(spring-boot-starter-data-jpa|spring-data-jpa|JpaRepository|CrudRepository|PagingAndSortingRepository)\b/u)) {
        addToolingHint('spring-data-jpa', 'Found Spring Data JPA dependency or repository evidence', 2);
    }
    if (configTextMatches(workspaceRoot, scan.files, /(^|\/)([^/]+\.csproj|packages\.config|[^/]+\.cs)$/iu, /\b(Microsoft\.EntityFrameworkCore|DbContext|DbSet<)/u)) {
        addToolingHint('entity-framework-core', 'Found Entity Framework Core dependency or DbContext evidence', 2);
    }
    if (configTextMatches(workspaceRoot, scan.files, /(^|\/)(requirements.*\.txt|pyproject\.toml|setup\.py|Pipfile|poetry\.lock|[^/]+\.py)$/iu, /\b(SQLAlchemy|sqlalchemy)\b/u)) {
        addToolingHint('sqlalchemy', 'Found SQLAlchemy dependency or import evidence', 2);
    }
    if (configTextMatches(workspaceRoot, scan.files, /(^|\/)(requirements.*\.txt|pyproject\.toml|setup\.py|Pipfile|poetry\.lock|alembic\.ini|[^/]+\.py)$/iu, /\b(alembic|Alembic)\b/u) ||
        fileExists(scan.files, 'alembic.ini')) {
        addToolingHint('alembic', 'Found Alembic dependency or migration config', 2);
    }
    if (composerDependencyExists(composerJsonSummaries, 'doctrine/orm')) {
        addToolingHint('doctrine-orm', 'Found composer.json with doctrine/orm dependency', 2);
    }
    if (composerDependencyExists(composerJsonSummaries, 'illuminate/database')) {
        addToolingHint('eloquent', 'Found composer.json with illuminate/database dependency', 2);
    }
    const envFilePath = scan.files.find((entry) => entry === '.env' || entry.endsWith('/.env'));
    if (hasPrismaEvidence && envFilePath) {
        const envText = safeReadText(path.join(workspaceRoot, ...envFilePath.split('/')), 20_000);
        if (envText && /\bDATABASE_URL\s*=/u.test(envText)) {
            addEvidence(evidence, `Found DATABASE_URL in ${envFilePath} with Prisma evidence`);
            score += 1;
        }
    }
    let confidence = 'low';
    if (score >= 5) {
        confidence = 'high';
    }
    else if (score >= 2) {
        confidence = 'medium';
    }
    return {
        toolingHints: uniqueSorted(toolingHints),
        databaseHints: uniqueSorted(databaseHints),
        confidence,
        evidence: uniqueSorted(evidence),
    };
}
//# sourceMappingURL=toolingHintDetection.js.map