"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.detectApiDbHints = detectApiDbHints;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const projectKindDetection_1 = require("./projectKindDetection");
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 uniqueSortedDatabaseKinds(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);
                }
            }
            summaries.push({ path: packageJsonPath, dependencies });
        }
        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 fileExists(files, name) {
    return files.some((entry) => entry === name || entry.endsWith(`/${name}`));
}
function directoryExists(directories, name) {
    return directories.some((entry) => entry === name || entry.endsWith(`/${name}`));
}
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 anyFileExists(files, names) {
    return names.some((name) => fileExists(files, name));
}
function getFileExtension(filePath) {
    const normalized = normalizePath(filePath);
    return path.posix.extname(normalized).toLowerCase();
}
function getFileNameWithoutExtension(filePath) {
    const normalized = normalizePath(filePath);
    const parsed = path.posix.parse(normalized);
    return parsed.name.toLowerCase();
}
function hasDirectorySegment(filePath, segment) {
    const normalized = normalizePath(filePath);
    return normalized.split('/').some((part) => part.toLowerCase() === segment.toLowerCase());
}
function detectApiHints(workspaceRoot, files, directories, packageJsonSummaries, composerJsonSummaries) {
    const evidence = [];
    let restScore = 0;
    let graphqlScore = 0;
    const addRest = (points, note) => {
        restScore += points;
        addEvidence(evidence, note);
    };
    const addGraphql = (points, note) => {
        graphqlScore += points;
        addEvidence(evidence, note);
    };
    for (const openApiFile of ['contracts/openapi.yaml', 'contracts/openapi.yml', 'openapi.yaml', 'openapi.yml']) {
        if (fileExists(files, openApiFile)) {
            addRest(4, `Found ${openApiFile}`);
        }
    }
    for (const swaggerFile of ['swagger.yaml', 'swagger.yml', 'swagger.json']) {
        if (fileExists(files, swaggerFile)) {
            addRest(4, `Found ${swaggerFile}`);
        }
    }
    for (const directoryName of ['controllers', 'routes', 'router']) {
        if (directoryExists(directories, directoryName)) {
            addRest(2, `Found ${directoryName} directory`);
        }
    }
    if (directoryExists(directories, 'app/api') || directoryExists(directories, 'src/app/api')) {
        addRest(2, 'Found Next.js app/api route directory');
    }
    if (files.some((entry) => /(^|\/)route\.[jt]s$/u.test(entry))) {
        addRest(2, 'Found route.ts/route.js file');
    }
    const laravelApiRoutePath = files.find((entry) => /(^|\/)routes\/api\.php$/iu.test(entry));
    if (laravelApiRoutePath) {
        const content = safeReadText(path.join(workspaceRoot, ...laravelApiRoutePath.split('/')), 30_000);
        if (content &&
            (/\bRoute::(?:get|post|put|patch|delete|apiResource|resource)\s*\(\s*['"][^'"]*[A-Za-z0-9_{]/u.test(content) ||
                /\bRoute::middleware\s*\(\s*['"]api['"]\s*\)/u.test(content) ||
                /\bRoute::prefix\s*\(\s*['"]api(?:\/|['"])/u.test(content))) {
            addRest(4, `Found meaningful Laravel API routes in ${laravelApiRoutePath}`);
        }
        else {
            addEvidence(evidence, `Found ${laravelApiRoutePath} without strong API route evidence`);
        }
    }
    for (const dependency of ['express', 'fastify', '@nestjs/core', 'koa', 'hapi']) {
        if (dependencyExists(packageJsonSummaries, dependency)) {
            addRest(1, `Found package.json with ${dependency} dependency`);
        }
    }
    for (const dependency of ['@apollo/server', 'apollo-server', 'graphql', 'graphql-yoga']) {
        if (dependencyExists(packageJsonSummaries, dependency)) {
            addGraphql(1, `Found package.json with ${dependency} dependency`);
        }
    }
    if (composerDependencyExists(composerJsonSummaries, 'laravel/framework')) {
        addEvidence(evidence, 'Found Laravel framework dependency without treating framework presence as API evidence');
    }
    for (const schemaFile of ['schema.graphql', 'schema.gql']) {
        if (fileExists(files, schemaFile)) {
            addGraphql(4, `Found ${schemaFile}`);
        }
    }
    if (directoryExists(directories, 'graphql') || directoryExists(directories, 'resolvers')) {
        addGraphql(2, 'Found graphql/ or resolvers/ directory');
    }
    // Lightweight code-content checks for framework hints.
    const apiCodeExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.java', '.kt', '.php']);
    const apiFileNamePattern = /(?:^|[-_.])(controller|route|router|resolver|graphql|endpoint|rest)(?:[-_.]|$)/iu;
    const candidateFiles = files
        .filter((entry) => apiCodeExtensions.has(getFileExtension(entry)))
        .filter((entry) => {
        const fileName = getFileNameWithoutExtension(entry);
        return (apiFileNamePattern.test(fileName) ||
            hasDirectorySegment(entry, 'controllers') ||
            hasDirectorySegment(entry, 'controller') ||
            hasDirectorySegment(entry, 'routes') ||
            hasDirectorySegment(entry, 'route') ||
            hasDirectorySegment(entry, 'router') ||
            hasDirectorySegment(entry, 'resolvers') ||
            hasDirectorySegment(entry, 'resolver') ||
            hasDirectorySegment(entry, 'graphql') ||
            hasDirectorySegment(entry, 'api'));
    })
        .slice(0, 220);
    for (const relativePath of candidateFiles) {
        const absolutePath = path.join(workspaceRoot, ...relativePath.split('/'));
        const text = safeReadText(absolutePath, 30_000);
        if (!text) {
            continue;
        }
        const extension = getFileExtension(relativePath);
        const isJavaLike = extension === '.java' || extension === '.kt';
        const isTypeScriptLike = extension === '.ts' || extension === '.tsx' || extension === '.js' || extension === '.jsx';
        const isPhp = extension === '.php';
        if (isJavaLike && /@RestController\b/u.test(text)) {
            addRest(2, `Found @RestController usage in ${relativePath}`);
        }
        if (isTypeScriptLike && /@(Controller|Get|Post|Put|Patch|Delete)\s*\(/u.test(text)) {
            addRest(2, `Found HTTP controller/route decorators in ${relativePath}`);
        }
        if (isPhp && (/\bresponse\s*\(\s*\)\s*->\s*json\s*\(/u.test(text) || /\bJsonResource\b/u.test(text))) {
            addRest(3, `Found Laravel JSON response/resource usage in ${relativePath}`);
        }
        if (/\btypeDefs\b/u.test(text) || /\bresolvers?\b/u.test(text)) {
            addGraphql(1, `Found GraphQL typeDefs/resolver hints in ${relativePath}`);
        }
        if (isJavaLike && (/@Query\b/u.test(text) || /@Mutation\b/u.test(text))) {
            addGraphql(2, `Found GraphQL Query/Mutation annotations in ${relativePath}`);
        }
    }
    let apiStyle = 'unknown';
    let confidence = 'low';
    if (restScore === 0 && graphqlScore === 0) {
        apiStyle = 'unknown';
        confidence = 'low';
    }
    else if (restScore >= graphqlScore && restScore >= 3) {
        apiStyle = 'rest';
        confidence = restScore >= 4 ? 'high' : 'medium';
        if (graphqlScore > 0) {
            addEvidence(evidence, 'Detected both REST and GraphQL signals; REST evidence is stronger');
        }
    }
    else if (graphqlScore >= 3) {
        apiStyle = 'graphql';
        confidence = graphqlScore >= 4 ? 'high' : 'medium';
        if (restScore > 0) {
            addEvidence(evidence, 'Detected both REST and GraphQL signals; GraphQL evidence is stronger');
        }
    }
    else {
        apiStyle = 'unknown';
        confidence = 'low';
        addEvidence(evidence, 'API evidence was present but not strong enough to select an API style');
    }
    return {
        result: {
            apiStyle,
            confidence,
            evidence: uniqueSorted(evidence),
        },
        restScore,
        graphqlScore,
    };
}
const primaryDatabaseKinds = new Set([
    'postgresql',
    'mysql',
    'mariadb',
    'mongodb',
    'sqlite',
    'sqlserver',
    'oracle',
    'cassandra',
    'dynamodb',
    'cosmosdb',
    'firestore',
    'neo4j',
    'snowflake',
    'bigquery',
]);
const databaseAliases = [
    { database: 'postgresql', aliases: ['pgsql', 'postgres', 'postgresql', 'jdbc:postgresql'] },
    { database: 'mysql', aliases: ['mysql', 'jdbc:mysql'] },
    { database: 'mariadb', aliases: ['mariadb', 'jdbc:mariadb'] },
    { database: 'mongodb', aliases: ['mongodb', 'mongo', 'mongodb+srv', 'mongodb://', 'mongodb+srv://'] },
    { database: 'sqlite', aliases: ['sqlite', 'sqlite3', '.sqlite', '.sqlite3'] },
    { database: 'sqlserver', aliases: ['sqlsrv', 'sqlserver', 'mssql', 'jdbc:sqlserver', 'pdo_sqlsrv'] },
    { database: 'oracle', aliases: ['oracle', 'oci8', 'pdo_oci', 'jdbc:oracle', 'SERVICE_NAME', 'TNS'] },
    { database: 'redis', aliases: ['redis', 'redis://', 'rediss://'] },
    { database: 'cassandra', aliases: ['cassandra', 'cql'] },
    { database: 'elasticsearch', aliases: ['elasticsearch', 'elastic.co'] },
    { database: 'opensearch', aliases: ['opensearch'] },
    { database: 'dynamodb', aliases: ['dynamodb', 'dynamo_db', 'aws_dynamodb'] },
    { database: 'cosmosdb', aliases: ['cosmosdb', 'cosmos_db', 'azure-cosmos', 'documentdb'] },
    { database: 'firestore', aliases: ['firestore', 'firebase-admin'] },
    { database: 'neo4j', aliases: ['neo4j', 'bolt://', 'neo4j://'] },
    { database: 'snowflake', aliases: ['snowflake'] },
    { database: 'bigquery', aliases: ['bigquery', 'big_query'] },
];
const packageDependencyDatabaseHints = [
    { database: 'postgresql', dependencies: ['pg', 'postgres', 'postgresql', 'typeorm', 'sequelize', 'knex', 'drizzle-orm'], note: 'SQL database' },
    { database: 'mysql', dependencies: ['mysql', 'mysql2'] },
    { database: 'mariadb', dependencies: ['mariadb'] },
    { database: 'mongodb', dependencies: ['mongoose', 'mongodb'] },
    { database: 'sqlite', dependencies: ['sqlite3', 'better-sqlite3'] },
    { database: 'sqlserver', dependencies: ['mssql', 'tedious'] },
    { database: 'oracle', dependencies: ['oracledb'] },
    { database: 'redis', dependencies: ['redis', 'ioredis'] },
    { database: 'cassandra', dependencies: ['cassandra-driver'] },
    { database: 'elasticsearch', dependencies: ['@elastic/elasticsearch', 'elasticsearch'] },
    { database: 'opensearch', dependencies: ['@opensearch-project/opensearch'] },
    { database: 'dynamodb', dependencies: ['@aws-sdk/client-dynamodb', 'aws-sdk', 'dynamodb'] },
    { database: 'cosmosdb', dependencies: ['@azure/cosmos'] },
    { database: 'firestore', dependencies: ['@google-cloud/firestore', 'firebase-admin'] },
    { database: 'neo4j', dependencies: ['neo4j-driver'] },
    { database: 'snowflake', dependencies: ['snowflake-sdk'] },
    { database: 'bigquery', dependencies: ['@google-cloud/bigquery'] },
];
const composerDependencyDatabaseHints = [
    { database: 'sqlserver', dependencies: ['ext-sqlsrv', 'ext-pdo_sqlsrv'] },
    { database: 'oracle', dependencies: ['ext-oci8', 'ext-pdo_oci', 'yajra/laravel-oci8'] },
    { database: 'mongodb', dependencies: ['mongodb/mongodb', 'ext-mongodb'] },
    { database: 'redis', dependencies: ['predis/predis', 'ext-redis'] },
    { database: 'postgresql', dependencies: ['ext-pgsql', 'ext-pdo_pgsql'] },
    { database: 'mysql', dependencies: ['ext-mysqli', 'ext-pdo_mysql'] },
    { database: 'sqlite', dependencies: ['ext-sqlite3', 'ext-pdo_sqlite'] },
];
const textDependencyDatabaseHints = [
    { database: 'postgresql', pattern: /\b(psycopg2|asyncpg|pg8000|org\.postgresql|Npgsql|gem ['"]pg['"])/iu, label: 'PostgreSQL dependency' },
    { database: 'mysql', pattern: /\b(mysqlclient|pymysql|mysql-connector|mysql2|mysql-connector-java|MySqlConnector|Pomelo\.EntityFrameworkCore\.MySql)\b/iu, label: 'MySQL dependency' },
    { database: 'mariadb', pattern: /\b(mariadb|mariadb-java-client)\b/iu, label: 'MariaDB dependency' },
    { database: 'mongodb', pattern: /\b(pymongo|motor|MongoDB\.Driver|mongoid|mongoose|mongodb)\b/iu, label: 'MongoDB dependency' },
    { database: 'sqlite', pattern: /\b(sqlite3|Microsoft\.Data\.Sqlite|System\.Data\.SQLite|sqlite)\b/iu, label: 'SQLite dependency' },
    { database: 'sqlserver', pattern: /\b(mssql-jdbc|sqljdbc|Microsoft\.Data\.SqlClient|System\.Data\.SqlClient|pyodbc|pymssql|tiny_tds)\b/iu, label: 'SQL Server dependency' },
    { database: 'oracle', pattern: /\b(ojdbc|oracledb|cx_Oracle|Oracle\.ManagedDataAccess|ruby-oci8)\b/iu, label: 'Oracle dependency' },
    { database: 'redis', pattern: /\b(redis|StackExchange\.Redis|Jedis|lettuce-core)\b/iu, label: 'Redis dependency' },
    { database: 'cassandra', pattern: /\b(cassandra-driver|cassandra|DataStax|CassandraCSharpDriver)\b/iu, label: 'Cassandra dependency' },
    { database: 'elasticsearch', pattern: /\b(elasticsearch|Elastic\.Clients\.Elasticsearch|Nest)\b/iu, label: 'Elasticsearch dependency' },
    { database: 'opensearch', pattern: /\b(opensearch|OpenSearch\.Client)\b/iu, label: 'OpenSearch dependency' },
    { database: 'dynamodb', pattern: /\b(dynamodb|DynamoDB|software\.amazon\.awssdk:dynamodb)\b/u, label: 'DynamoDB dependency' },
    { database: 'cosmosdb', pattern: /\b(azure-cosmos|Microsoft\.Azure\.Cosmos|com\.azure:azure-cosmos)\b/iu, label: 'Cosmos DB dependency' },
    { database: 'firestore', pattern: /\b(firestore|firebase-admin|google-cloud-firestore)\b/iu, label: 'Firestore dependency' },
    { database: 'neo4j', pattern: /\b(neo4j-driver|neo4j|org\.neo4j\.driver)\b/iu, label: 'Neo4j dependency' },
    { database: 'snowflake', pattern: /\b(snowflake-connector|snowflake-sqlalchemy|snowflake-jdbc|Snowflake\.Data|snowflake-sdk)\b/iu, label: 'Snowflake dependency' },
    { database: 'bigquery', pattern: /\b(bigquery|google-cloud-bigquery|Google\.Cloud\.BigQuery)\b/iu, label: 'BigQuery dependency' },
];
const ormToolingPatterns = [
    /\b(typeorm|sequelize|drizzle-orm|@mikro-orm\/core|prisma|@prisma\/client)\b/iu,
    /\b(hibernate-core|org\.hibernate|spring-boot-starter-data-jpa|JpaRepository|CrudRepository|jakarta\.persistence|javax\.persistence)\b/u,
    /\b(Microsoft\.EntityFrameworkCore|DbContext|DbSet<)\b/u,
    /\b(SQLAlchemy|sqlalchemy|alembic|Alembic)\b/u,
    /\b(doctrine\/orm|illuminate\/database)\b/iu,
];
function normalizeConnectionName(value) {
    const normalized = value.trim().toLowerCase().replace(/['"]/gu, '');
    for (const entry of databaseAliases) {
        if (entry.aliases.some((alias) => normalized === alias.toLowerCase())) {
            return entry.database;
        }
    }
    return undefined;
}
function detectDatabasesInText(content) {
    const found = [];
    const lower = content.toLowerCase();
    for (const entry of databaseAliases) {
        if (entry.aliases.some((alias) => lower.includes(alias.toLowerCase()))) {
            found.push(entry.database);
        }
    }
    if (/\bserver\s*=[^;\n]+;\s*database\s*=/iu.test(content)) {
        found.push('sqlserver');
    }
    if (/\bdata source\s*=[^;\n]+;\s*(initial catalog|database)\s*=/iu.test(content)) {
        found.push('sqlserver');
    }
    if (/\bData Source\s*=\s*[^;\n]+\.(sqlite|sqlite3|db)\b/u.test(content)) {
        found.push('sqlite');
    }
    return uniqueSortedDatabaseKinds(found);
}
function detectDatabaseHints(workspaceRoot, files, directories, packageJsonSummaries, composerJsonSummaries) {
    const evidence = [];
    let hasUnknownDatabaseEvidence = false;
    const scores = {
        postgresql: 0,
        mysql: 0,
        mariadb: 0,
        mongodb: 0,
        sqlite: 0,
        sqlserver: 0,
        oracle: 0,
        redis: 0,
        cassandra: 0,
        elasticsearch: 0,
        opensearch: 0,
        dynamodb: 0,
        cosmosdb: 0,
        firestore: 0,
        neo4j: 0,
        snowflake: 0,
        bigquery: 0,
    };
    const addDb = (database, points, note) => {
        scores[database] += points;
        addEvidence(evidence, note);
    };
    // Prisma
    const prismaSchemaPath = files.find((entry) => entry === 'prisma/schema.prisma' || entry.endsWith('/prisma/schema.prisma'));
    if (prismaSchemaPath) {
        const content = safeReadText(path.join(workspaceRoot, ...prismaSchemaPath.split('/')));
        if (content) {
            if (/provider\s*=\s*["']postgresql["']/iu.test(content)) {
                addDb('postgresql', 4, 'Found Prisma datasource provider postgresql');
            }
            if (/provider\s*=\s*["']mysql["']/iu.test(content)) {
                addDb('mysql', 4, 'Found Prisma datasource provider mysql');
            }
            if (/provider\s*=\s*["']mariadb["']/iu.test(content)) {
                addDb('mariadb', 4, 'Found Prisma datasource provider mariadb');
            }
            if (/provider\s*=\s*["']mongodb["']/iu.test(content)) {
                addDb('mongodb', 4, 'Found Prisma datasource provider mongodb');
            }
            if (/provider\s*=\s*["']sqlite["']/iu.test(content)) {
                addDb('sqlite', 4, 'Found Prisma datasource provider sqlite');
            }
            if (/provider\s*=\s*["']sqlserver["']/iu.test(content)) {
                addDb('sqlserver', 4, 'Found Prisma datasource provider sqlserver');
            }
        }
    }
    // Generic datasource configs / content hints
    const configCandidateFiles = files.filter((entry) => /(^|\/)(application\.(properties|ya?ml)|appsettings(\.[A-Za-z0-9_-]+)?\.json|settings\.py|database\.ya?ml|database\.yml|schema\.prisma|ormconfig\.(json|js|ts)|knexfile\.(js|ts)|sequelize\.(js|ts)|sequelize\/config\.(js|ts)|config\/database\.php|\.env(\.[a-z0-9_-]+)?)$/iu.test(entry) ||
        /(^|\/)(docker-compose|compose)(\.[A-Za-z0-9_-]+)?\.ya?ml$/iu.test(entry) ||
        /(^|\/)(package\.json|composer\.json|pom\.xml|build\.gradle(\.kts)?|requirements\.txt|pyproject\.toml|Gemfile|[^/]+\.csproj)$/iu.test(entry));
    for (const relativePath of configCandidateFiles.slice(0, 120)) {
        const content = safeReadText(path.join(workspaceRoot, ...relativePath.split('/')), 50_000);
        if (!content) {
            continue;
        }
        const isLaravelDatabaseConfig = /(^|\/)config\/database\.php$/iu.test(relativePath);
        const dbConnectionMatch = content.match(/(?:^|\n)\s*DB_CONNECTION\s*=\s*([A-Za-z0-9_-]+)/iu);
        const laravelDefaultConnectionMatch = content.match(/['"]default['"]\s*=>\s*env\s*\(\s*['"]DB_CONNECTION['"]\s*,\s*['"]([^'"]+)['"]\s*\)/iu);
        const laravelDefaultConnection = (laravelDefaultConnectionMatch?.[1] ?? '').toLowerCase();
        const shouldTrustLaravelDefaultConnection = isLaravelDatabaseConfig &&
            ['sqlsrv', 'sqlserver', 'mssql', 'oracle', 'oci8'].includes(laravelDefaultConnection);
        const explicitConnection = (dbConnectionMatch?.[1] ??
            (shouldTrustLaravelDefaultConnection ? laravelDefaultConnection : '') ??
            '').toLowerCase();
        const normalizedExplicitConnection = normalizeConnectionName(explicitConnection);
        if (normalizedExplicitConnection) {
            addDb(normalizedExplicitConnection, 4, `Found explicit ${normalizedExplicitConnection} DB connection in ${relativePath}`);
        }
        else if (dbConnectionMatch?.[1]) {
            hasUnknownDatabaseEvidence = true;
            addEvidence(evidence, `Found explicit unmapped DB connection in ${relativePath}`);
        }
        if (explicitConnection === 'pgsql' || explicitConnection === 'postgres' || explicitConnection === 'postgresql') {
            addDb('postgresql', 4, `Found explicit PostgreSQL DB connection in ${relativePath}`);
        }
        if (explicitConnection === 'mysql') {
            addDb('mysql', 4, `Found explicit MySQL DB connection in ${relativePath}`);
        }
        if (explicitConnection === 'mariadb') {
            addDb('mariadb', 4, `Found explicit MariaDB DB connection in ${relativePath}`);
        }
        if (explicitConnection === 'sqlite') {
            addDb('sqlite', 4, `Found explicit SQLite DB connection in ${relativePath}`);
        }
        if (explicitConnection === 'sqlsrv' || explicitConnection === 'sqlserver' || explicitConnection === 'mssql') {
            addDb('sqlserver', 4, `Found explicit SQL Server DB connection in ${relativePath}`);
        }
        if (explicitConnection === 'oracle' || explicitConnection === 'oci8') {
            addDb('oracle', 4, `Found explicit Oracle DB connection in ${relativePath}`);
        }
        if (isLaravelDatabaseConfig) {
            if (/\b(sqlsrv|pdo_sqlsrv|mssql|sqlserver)\b/iu.test(content)) {
                addEvidence(evidence, `Found SQL Server-capable Laravel database config in ${relativePath}`);
            }
            if (/\b(oci8|pdo_oci|oracle|SERVICE_NAME|SID|TNS)\b/u.test(content)) {
                addEvidence(evidence, `Found Oracle-capable Laravel database config in ${relativePath}`);
            }
            continue;
        }
        const envLikeMatches = content.matchAll(/(?:^|\n)\s*([A-Z0-9_]*(?:DATABASE_URL|DB_URL|DB_HOST|DB_PORT|DB_DATABASE)|SQLSERVER_[A-Z0-9_]+|MSSQL_[A-Z0-9_]+|ORACLE_[A-Z0-9_]+|MONGO(?:DB)?_[A-Z0-9_]+|REDIS_[A-Z0-9_]+|CASSANDRA_[A-Z0-9_]+|ELASTICSEARCH_[A-Z0-9_]+|OPENSEARCH_[A-Z0-9_]+|DYNAMODB_[A-Z0-9_]+|COSMOS(?:DB)?_[A-Z0-9_]+|FIRESTORE_[A-Z0-9_]+|NEO4J_[A-Z0-9_]+|SNOWFLAKE_[A-Z0-9_]+|BIGQUERY_[A-Z0-9_]+)\s*=\s*([^\n#]+)/giu);
        for (const match of envLikeMatches) {
            const databases = detectDatabasesInText(`${match[1]}=${match[2]}`);
            if (databases.length === 0 && /(?:DATABASE_URL|DB_URL|DB_CONNECTION)/iu.test(match[1])) {
                hasUnknownDatabaseEvidence = true;
                addEvidence(evidence, `Found unmapped database environment/config variable in ${relativePath}`);
            }
            for (const database of databases) {
                addDb(database, 3, `Found ${database} environment/config variable in ${relativePath}`);
            }
        }
        if (/jdbc:postgresql:\/\//iu.test(content) || /\bpostgres(?:ql)?\b/iu.test(content)) {
            addDb('postgresql', 2, `Found PostgreSQL datasource/config hints in ${relativePath}`);
        }
        if (/jdbc:mysql:\/\//iu.test(content) || /\bmysql\b/iu.test(content)) {
            addDb('mysql', 2, `Found MySQL datasource/config hints in ${relativePath}`);
        }
        if (/jdbc:mariadb:\/\//iu.test(content) || /\bmariadb\b/iu.test(content)) {
            addDb('mariadb', 2, `Found MariaDB datasource/config hints in ${relativePath}`);
        }
        if (/mongodb(\+srv)?:\/\//iu.test(content) || /\bmongodb\b/iu.test(content)) {
            addDb('mongodb', 2, `Found MongoDB datasource/config hints in ${relativePath}`);
        }
        if (/\bsqlite\b/iu.test(content) || /\.sqlite\b/iu.test(content)) {
            addDb('sqlite', 2, `Found SQLite datasource/config hints in ${relativePath}`);
        }
        if (/jdbc:sqlserver:\/\//iu.test(content) || /\b(sqlsrv|pdo_sqlsrv|mssql|sqlserver)\b/iu.test(content)) {
            addDb('sqlserver', 2, `Found SQL Server datasource/config hints in ${relativePath}`);
        }
        if (/jdbc:oracle:thin:@/iu.test(content) || /\b(oci8|pdo_oci|oracle|SERVICE_NAME|SID|TNS)\b/u.test(content)) {
            addDb('oracle', 2, `Found Oracle datasource/config hints in ${relativePath}`);
        }
        for (const database of detectDatabasesInText(content)) {
            addDb(database, 1, `Found ${database} connection/config text in ${relativePath}`);
        }
        for (const dependencyHint of textDependencyDatabaseHints) {
            if (dependencyHint.pattern.test(content)) {
                addDb(dependencyHint.database, 2, `Found ${dependencyHint.label} in ${relativePath}`);
            }
        }
    }
    for (const dependency of ['pg', 'postgres', 'postgresql']) {
        if (dependencyExists(packageJsonSummaries, dependency)) {
            addDb('postgresql', 1, `Found package.json dependency hint (${dependency})`);
        }
    }
    for (const dependency of ['mysql', 'mysql2']) {
        if (dependencyExists(packageJsonSummaries, dependency)) {
            addDb('mysql', 1, `Found package.json dependency hint (${dependency})`);
        }
    }
    for (const dependency of ['mongoose', 'mongodb']) {
        if (dependencyExists(packageJsonSummaries, dependency)) {
            addDb('mongodb', 1, `Found package.json dependency hint (${dependency})`);
        }
    }
    for (const dependency of ['sqlite3', 'better-sqlite3']) {
        if (dependencyExists(packageJsonSummaries, dependency)) {
            addDb('sqlite', 1, `Found package.json dependency hint (${dependency})`);
        }
    }
    if (dependencyExists(packageJsonSummaries, 'mssql')) {
        addDb('sqlserver', 1, 'Found package.json dependency hint (mssql)');
    }
    if (dependencyExists(packageJsonSummaries, 'oracledb')) {
        addDb('oracle', 1, 'Found package.json dependency hint (oracledb)');
    }
    for (const databaseHint of packageDependencyDatabaseHints) {
        for (const dependency of databaseHint.dependencies) {
            if (dependencyExists(packageJsonSummaries, dependency)) {
                addDb(databaseHint.database, 1, databaseHint.note
                    ? `Found package.json dependency hint (${dependency}) for ${databaseHint.note}`
                    : `Found package.json dependency hint (${dependency})`);
            }
        }
    }
    for (const dependency of ['ext-sqlsrv', 'ext-pdo_sqlsrv']) {
        if (composerDependencyExists(composerJsonSummaries, dependency)) {
            addDb('sqlserver', 3, `Found composer.json dependency hint (${dependency})`);
        }
    }
    for (const dependency of ['ext-oci8', 'ext-pdo_oci', 'yajra/laravel-oci8']) {
        if (composerDependencyExists(composerJsonSummaries, dependency)) {
            addDb('oracle', 3, `Found composer.json dependency hint (${dependency})`);
        }
    }
    for (const databaseHint of composerDependencyDatabaseHints) {
        for (const dependency of databaseHint.dependencies) {
            if (composerDependencyExists(composerJsonSummaries, dependency)) {
                addDb(databaseHint.database, 2, `Found composer.json dependency hint (${dependency})`);
            }
        }
    }
    const hasRelationalOrmEvidence = ['typeorm', 'sequelize', '@sequelize/core', 'knex', 'drizzle-orm', '@mikro-orm/core', 'mikro-orm', 'prisma', '@prisma/client']
        .some((dependency) => dependencyExists(packageJsonSummaries, dependency)) ||
        ['doctrine/orm', 'illuminate/database'].some((dependency) => composerDependencyExists(composerJsonSummaries, dependency)) ||
        configCandidateFiles.some((relativePath) => {
            const content = safeReadText(path.join(workspaceRoot, ...relativePath.split('/')), 50_000);
            return content ? ormToolingPatterns.some((pattern) => pattern.test(content)) : false;
        });
    for (const relativePath of configCandidateFiles.slice(0, 120)) {
        const content = safeReadText(path.join(workspaceRoot, ...relativePath.split('/')), 50_000);
        if (!content) {
            continue;
        }
        if (/\bNpgsql\.EntityFrameworkCore\.PostgreSQL\b/u.test(content)) {
            addDb('postgresql', 4, `Found Entity Framework PostgreSQL provider in ${relativePath}`);
        }
        if (/\bMicrosoft\.EntityFrameworkCore\.SqlServer\b/u.test(content)) {
            addDb('sqlserver', 4, `Found Entity Framework SQL Server provider in ${relativePath}`);
        }
        if (/\b(Pomelo\.EntityFrameworkCore\.MySql|MySql\.EntityFrameworkCore)\b/u.test(content)) {
            addDb('mysql', 4, `Found Entity Framework MySQL provider in ${relativePath}`);
        }
        if (/\bSqlite\b/u.test(content) && /\bMicrosoft\.EntityFrameworkCore\b/u.test(content)) {
            addDb('sqlite', 3, `Found Entity Framework SQLite provider in ${relativePath}`);
        }
    }
    if (hasRelationalOrmEvidence) {
        const primaryScores = Object.entries(scores)
            .filter(([database, score]) => primaryDatabaseKinds.has(database) && score > 0);
        const onlyPrimaryScore = primaryScores.length === 1 ? primaryScores[0] : undefined;
        if (onlyPrimaryScore) {
            addDb(onlyPrimaryScore[0], 1, `Found ORM/data-mapper evidence supporting ${onlyPrimaryScore[0]} database selection`);
        }
    }
    if (directoryExists(directories, 'db') || directoryExists(directories, 'migrations')) {
        addEvidence(evidence, 'Found db/ or migrations/ directory');
    }
    const ranked = Object.entries(scores)
        .sort((left, right) => {
        if (right[1] !== left[1]) {
            return right[1] - left[1];
        }
        return left[0].localeCompare(right[0]);
    });
    const [topDatabase, topScore] = ranked[0] ?? ['postgresql', 0];
    const secondScore = ranked[1]?.[1] ?? 0;
    const databaseHints = ranked
        .filter((entry) => entry[1] > 0)
        .map((entry) => entry[0]);
    let database = 'unknown';
    let confidence = 'low';
    if (topScore <= 0) {
        database = 'unknown';
        confidence = 'low';
    }
    else if (!primaryDatabaseKinds.has(topDatabase)) {
        database = 'unknown';
        confidence = topScore >= 3 ? 'medium' : 'low';
        addEvidence(evidence, `Detected ${topDatabase} as a data-store/search/cache hint, not a primary database selection`);
    }
    else if (secondScore > 0 && topScore < secondScore + 2) {
        database = 'unknown';
        confidence = 'medium';
        addEvidence(evidence, 'Detected multiple competing database hints; leaving primary database unselected');
    }
    else {
        database = topDatabase;
        if (topScore >= 4 && topScore >= secondScore + 1) {
            confidence = 'high';
        }
        else if (topScore >= 2) {
            confidence = 'medium';
        }
        else {
            confidence = 'low';
        }
    }
    return {
        result: {
            database,
            ...(databaseHints.length > 0 ? { databaseHints } : {}),
            ...(hasUnknownDatabaseEvidence ? { hasUnknownDatabaseEvidence: true } : {}),
            confidence,
            evidence: uniqueSorted(evidence),
        },
        scores,
    };
}
function detectApiDbHints(workspaceRoot, options) {
    const scan = scanWorkspace(workspaceRoot, 7, options?.ignoreMatcher);
    const packageJsonSummaries = parsePackageJsonSummaries(workspaceRoot, scan.files);
    const composerJsonSummaries = parseComposerJsonSummaries(workspaceRoot, scan.files);
    const apiDetection = detectApiHints(workspaceRoot, scan.files, scan.directories, packageJsonSummaries, composerJsonSummaries);
    const dbDetection = detectDatabaseHints(workspaceRoot, scan.files, scan.directories, packageJsonSummaries, composerJsonSummaries);
    const projectKinds = options?.projectKinds ?? (0, projectKindDetection_1.detectProjectKinds)(workspaceRoot);
    const apiResult = { ...apiDetection.result };
    const dbResult = { ...dbDetection.result };
    const likelyNoPublicApi = projectKinds.primaryKind === 'library' || projectKinds.primaryKind === 'cli';
    if (apiResult.apiStyle === 'unknown' && likelyNoPublicApi) {
        apiResult.apiStyle = 'none';
        apiResult.confidence = 'medium';
        addEvidence(apiResult.evidence, 'Detected library/cli-oriented repository with no API artifacts');
    }
    const likelyNoDatabase = projectKinds.primaryKind === 'frontend' ||
        projectKinds.primaryKind === 'library' ||
        projectKinds.primaryKind === 'cli';
    if (dbResult.database === 'unknown' && likelyNoDatabase) {
        dbResult.database = 'none';
        dbResult.confidence = 'medium';
        addEvidence(dbResult.evidence, 'Detected frontend/library/cli-oriented repository with no database artifacts');
    }
    const overallEvidence = [];
    if (apiResult.apiStyle === 'rest') {
        addEvidence(overallEvidence, 'Detected REST API signals');
    }
    if (apiResult.apiStyle === 'graphql') {
        addEvidence(overallEvidence, 'Detected GraphQL API signals');
    }
    if (dbResult.database !== 'unknown' && dbResult.database !== 'none') {
        addEvidence(overallEvidence, `Detected ${dbResult.database} database signals`);
    }
    if (apiResult.apiStyle === 'none') {
        addEvidence(overallEvidence, 'No API artifacts detected for current project profile');
    }
    if (dbResult.database === 'none') {
        addEvidence(overallEvidence, 'No database artifacts detected for current project profile');
    }
    if (overallEvidence.length === 0) {
        addEvidence(overallEvidence, 'API and database hints remain unknown from current repository evidence');
    }
    let confidence = 'low';
    if (apiResult.confidence === 'high' || dbResult.confidence === 'high') {
        confidence = 'high';
    }
    else if (apiResult.confidence === 'medium' || dbResult.confidence === 'medium') {
        confidence = 'medium';
    }
    return {
        api: {
            ...apiResult,
            evidence: uniqueSorted(apiResult.evidence),
        },
        database: {
            ...dbResult,
            evidence: uniqueSorted(dbResult.evidence),
        },
        confidence,
        evidence: uniqueSorted(overallEvidence),
    };
}
//# sourceMappingURL=apiDbHintDetection.js.map