// Planu — Language detection: 42+ languages with version extraction
import { readFile } from 'node:fs/promises';
import { extname, join } from 'node:path';
import { glob } from 'glob';
import { hasAnyFile, readJsonFile, readTextFile, IGNORE_DIRS } from './detection-utils.js';
export { detectPythonPackageManager } from './detectors/python-pm-detector.js';
export { detectJsRuntime } from './detectors/js-runtime-detector.js';
/**
 * Detect project primary language from config files and file extensions.
 */
export async function detectLanguage(projectPath, signatures) {
    // Check config files first (most reliable)
    for (const [lang, sig] of Object.entries(signatures.languages)) {
        for (const file of sig.files) {
            if (await hasAnyFile(projectPath, [file])) {
                return lang;
            }
        }
    }
    // Fallback: count files by extension
    return detectLanguageByExtension(projectPath, signatures);
}
async function detectLanguageByExtension(projectPath, signatures) {
    const extCounts = new Map();
    try {
        const files = await glob('**/*', {
            cwd: projectPath,
            nodir: true,
            ignore: IGNORE_DIRS,
            maxDepth: 4,
        });
        for (const file of files) {
            const ext = extname(file);
            if (ext) {
                extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
            }
        }
    }
    catch {
        // Ignore glob errors
    }
    for (const [lang, sig] of Object.entries(signatures.languages)) {
        for (const ext of sig.extensions) {
            if ((extCounts.get(ext) ?? 0) > 0) {
                return lang;
            }
        }
    }
    return 'unknown';
}
/**
 * Detect the exact version of the project's primary language.
 * STRATEGY PATTERN: pluggable detector functions per language, selected at runtime.
 * See DESIGN_PATTERNS.md § Strategy Pattern.
 */
export async function detectLanguageVersion(projectPath, language) {
    const detectors = {
        typescript: () => detectTsVersion(projectPath),
        javascript: () => detectNodeVersion(projectPath),
        python: () => detectPythonVersion(projectPath),
        go: () => detectGoVersion(projectPath),
        rust: () => detectRustVersion(projectPath),
        java: () => detectJavaVersion(projectPath),
        kotlin: () => detectJavaVersion(projectPath),
        ruby: () => detectRubyVersion(projectPath),
        php: () => detectPhpVersion(projectPath),
        swift: () => detectSwiftVersion(projectPath),
        dart: () => detectDartVersion(projectPath),
        elixir: () => detectElixirVersion(projectPath),
    };
    const detector = detectors[language];
    if (!detector) {
        return null;
    }
    return detector();
}
async function detectTsVersion(projectPath) {
    const pkgJson = await readJsonFile(join(projectPath, 'package.json'));
    if (!pkgJson) {
        return null;
    }
    const devDeps = pkgJson.devDependencies;
    const deps = pkgJson.dependencies;
    return devDeps?.typescript ?? deps?.typescript ?? null;
}
async function detectNodeVersion(projectPath) {
    // .nvmrc
    const nvmrc = await readTextFile(join(projectPath, '.nvmrc'));
    if (nvmrc) {
        return nvmrc.trim();
    }
    // .node-version
    const nodeVersion = await readTextFile(join(projectPath, '.node-version'));
    if (nodeVersion) {
        return nodeVersion.trim();
    }
    // package.json engines.node
    const pkgJson = await readJsonFile(join(projectPath, 'package.json'));
    if (pkgJson) {
        const engines = pkgJson.engines;
        if (engines?.node) {
            return engines.node;
        }
    }
    return null;
}
async function detectPythonVersion(projectPath) {
    // .python-version
    const pyVersion = await readTextFile(join(projectPath, '.python-version'));
    if (pyVersion) {
        return pyVersion.trim();
    }
    // pyproject.toml requires-python
    const pyproject = await readTextFile(join(projectPath, 'pyproject.toml'));
    if (pyproject) {
        const match = /requires-python\s*=\s*"([^"]+)"/.exec(pyproject);
        if (match?.[1]) {
            return match[1];
        }
    }
    // runtime.txt
    const runtime = await readTextFile(join(projectPath, 'runtime.txt'));
    if (runtime) {
        const match = /python-(\S+)/.exec(runtime.trim());
        if (match?.[1]) {
            return match[1];
        }
    }
    return null;
}
async function detectGoVersion(projectPath) {
    try {
        const content = await readFile(join(projectPath, 'go.mod'), 'utf-8');
        const match = /^go\s+(\S+)/m.exec(content);
        if (match?.[1]) {
            return match[1];
        }
    }
    catch {
        // No go.mod
    }
    return null;
}
async function detectRustVersion(projectPath) {
    // rust-toolchain.toml
    const toolchain = await readTextFile(join(projectPath, 'rust-toolchain.toml'));
    if (toolchain) {
        const match = /channel\s*=\s*"([^"]+)"/.exec(toolchain);
        if (match?.[1]) {
            return match[1];
        }
    }
    // rust-toolchain
    const toolchainFile = await readTextFile(join(projectPath, 'rust-toolchain'));
    if (toolchainFile) {
        return toolchainFile.trim();
    }
    // Cargo.toml edition
    const cargo = await readTextFile(join(projectPath, 'Cargo.toml'));
    if (cargo) {
        const match = /edition\s*=\s*"([^"]+)"/.exec(cargo);
        if (match?.[1]) {
            return `edition-${match[1]}`;
        }
    }
    return null;
}
async function detectJavaVersion(projectPath) {
    // pom.xml
    const pom = await readTextFile(join(projectPath, 'pom.xml'));
    if (pom) {
        const match = /<java.version>(\S+)<\/java.version>/.exec(pom);
        if (match?.[1]) {
            return match[1];
        }
        const sourceMatch = /<maven.compiler.source>([^<]+)</.exec(pom);
        if (sourceMatch?.[1]) {
            return sourceMatch[1];
        }
    }
    // build.gradle / build.gradle.kts
    for (const gradleFile of ['build.gradle.kts', 'build.gradle']) {
        const gradle = await readTextFile(join(projectPath, gradleFile));
        if (gradle) {
            const match = /(?:sourceCompatibility|jvmTarget)\s*[=:]\s*['"]?(\S+?)['"]?\s/m.exec(gradle);
            if (match?.[1]) {
                return match[1];
            }
        }
    }
    return null;
}
async function detectRubyVersion(projectPath) {
    const rubyVersion = await readTextFile(join(projectPath, '.ruby-version'));
    if (rubyVersion) {
        return rubyVersion.trim();
    }
    const gemfile = await readTextFile(join(projectPath, 'Gemfile'));
    if (gemfile) {
        const match = /ruby\s+['"]([^'"]+)['"]/m.exec(gemfile);
        if (match?.[1]) {
            return match[1];
        }
    }
    return null;
}
async function detectPhpVersion(projectPath) {
    const composer = await readJsonFile(join(projectPath, 'composer.json'));
    if (composer) {
        const require = composer.require;
        if (require?.php) {
            return require.php;
        }
    }
    return null;
}
async function detectSwiftVersion(projectPath) {
    const swiftVersion = await readTextFile(join(projectPath, '.swift-version'));
    if (swiftVersion) {
        return swiftVersion.trim();
    }
    const pkg = await readTextFile(join(projectPath, 'Package.swift'));
    if (pkg) {
        const match = /swift-tools-version:\s*(\S+)/.exec(pkg);
        if (match?.[1]) {
            return match[1];
        }
    }
    return null;
}
async function detectDartVersion(projectPath) {
    const pubspec = await readTextFile(join(projectPath, 'pubspec.yaml'));
    if (pubspec) {
        const match = /sdk:\s*['"]?>=?\s*(\S+)/m.exec(pubspec);
        if (match?.[1]) {
            return match[1];
        }
    }
    return null;
}
async function detectElixirVersion(projectPath) {
    const mixExs = await readTextFile(join(projectPath, 'mix.exs'));
    if (mixExs) {
        const match = /elixir:\s*"([^"]+)"/.exec(mixExs);
        if (match?.[1]) {
            return match[1];
        }
    }
    return null;
}
//# sourceMappingURL=language-detector.js.map