"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PythonCodeZipPackagerSync = exports.PythonCodeZipPackager = exports.PLATFORM_CANDIDATES = void 0;
exports.extractPythonVersion = extractPythonVersion;
const schema_1 = require("../../schema");
const constants_1 = require("../constants");
const types_1 = require("../errors/types");
const subprocess_1 = require("../utils/subprocess");
const helpers_1 = require("./helpers");
const uv_1 = require("./uv");
const path_1 = require("path");
// eslint-disable-next-line security/detect-unsafe-regex -- bounded input from RuntimeVersion enum, not user input
const PYTHON_RUNTIME_REGEX = /PYTHON_(\d+)_?(\d+)?/;
/**
 * Type guard to check if runtime version is a Python runtime
 */
function isPythonRuntimeVersion(version) {
    return (0, helpers_1.isPythonRuntime)(version);
}
// AC Runtime uses AL2023 with GLIBC 2.34, we can support any manylinux <= 2_34
exports.PLATFORM_CANDIDATES = ['aarch64-manylinux2014', 'aarch64-manylinux_2_28', 'aarch64-manylinux_2_34'];
/**
 * Extracts Python version from runtime constant.
 * Example: PYTHON_3_12 -> "3.12" (for use with uv --python-version)
 */
function extractPythonVersion(runtime) {
    const match = PYTHON_RUNTIME_REGEX.exec(runtime);
    if (!match) {
        throw new types_1.PackagingError(`Unsupported Python runtime value: ${runtime}`);
    }
    const [, major, minor] = match;
    if (!major || !minor) {
        throw new types_1.PackagingError(`Invalid Python runtime value: ${runtime}`);
    }
    return `${Number(major)}.${Number(minor)}`;
}
/**
 * Async Python packager for CLI usage.
 */
class PythonCodeZipPackager {
    async pack(spec, options = {}) {
        if (spec.build !== 'CodeZip') {
            throw new types_1.PackagingError('Python packager only supports CodeZip build type.');
        }
        if (!isPythonRuntimeVersion(spec.runtimeVersion)) {
            throw new types_1.PackagingError(`Python packager only supports Python runtimes. Received: ${spec.runtimeVersion}`);
        }
        const agentName = options.agentName ?? spec.name;
        const { projectRoot, srcDir, stagingDir, artifactsDir, pyprojectPath } = await (0, helpers_1.resolveProjectPaths)(options, agentName);
        const pythonPlatforms = options.pythonPlatform ? [options.pythonPlatform] : exports.PLATFORM_CANDIDATES;
        await (0, helpers_1.ensureBinaryAvailable)('uv', constants_1.UV_INSTALL_HINT);
        await (0, helpers_1.ensureDirClean)(stagingDir);
        const finalStaging = await this.installWithRetries(projectRoot, srcDir, stagingDir, spec.runtimeVersion, pythonPlatforms, pyprojectPath);
        const artifactPath = options.outputPath ?? (0, path_1.join)(artifactsDir, (0, constants_1.getArtifactZipName)(agentName));
        await (0, helpers_1.createZipFromDir)(finalStaging, artifactPath);
        const sizeBytes = await (0, helpers_1.enforceZipSizeLimit)(artifactPath);
        return {
            artifactPath,
            sizeBytes,
            stagingPath: finalStaging,
        };
    }
    async installWithRetries(projectRoot, srcDir, stagingDir, runtimeVersion, pythonPlatforms, pyprojectPath) {
        const pythonVersion = extractPythonVersion(runtimeVersion);
        for (const platform of pythonPlatforms) {
            if (!platform) {
                throw new types_1.PackagingError('No platform candidate available for dependency installation.');
            }
            await (0, helpers_1.ensureDirClean)(stagingDir);
            const result = await (0, subprocess_1.runSubprocessCapture)('uv', [
                'pip',
                'install',
                '-r',
                pyprojectPath,
                '--target',
                stagingDir,
                '--python-version',
                pythonVersion,
                '--python-platform',
                platform,
                '--only-binary',
                ':all:',
            ], { cwd: projectRoot });
            if (result.code === 0) {
                await (0, helpers_1.copySourceTree)(srcDir, stagingDir);
                await (0, helpers_1.convertWindowsScriptsToLinux)(stagingDir);
                return stagingDir;
            }
            else {
                const platformIssue = (0, uv_1.detectUnavailablePlatform)(result);
                if (platformIssue) {
                    continue;
                }
                const combined = `${result.stdout}\n${result.stderr}`.trim();
                throw new types_1.PackagingError(combined.length > 0 ? combined : `uv install failed on platform ${platform} with exit code ${result.code}`);
            }
        }
        throw new types_1.PackagingError('uv install failed for all platform candidates.');
    }
}
exports.PythonCodeZipPackager = PythonCodeZipPackager;
/**
 * Sync Python packager for CDK bundling.
 */
class PythonCodeZipPackagerSync {
    packCodeZip(config, options = {}) {
        const runtimeVersion = config.runtimeVersion ?? schema_1.DEFAULT_PYTHON_VERSION;
        if (!isPythonRuntimeVersion(runtimeVersion)) {
            throw new types_1.PackagingError(`Python packager only supports Python runtimes. Received: ${runtimeVersion}`);
        }
        const agentName = options.agentName ?? config.name ?? 'asset';
        const { projectRoot, srcDir, stagingDir, artifactsDir, pyprojectPath } = (0, helpers_1.resolveProjectPathsSync)(options, agentName);
        const pythonPlatforms = options.pythonPlatform ? [options.pythonPlatform] : exports.PLATFORM_CANDIDATES;
        (0, helpers_1.ensureBinaryAvailableSync)('uv', constants_1.UV_INSTALL_HINT);
        (0, helpers_1.ensureDirCleanSync)(stagingDir);
        const finalStaging = this.installWithRetriesSync(projectRoot, srcDir, stagingDir, runtimeVersion, pythonPlatforms, pyprojectPath);
        const artifactPath = options.outputPath ?? (0, path_1.join)(artifactsDir, (0, constants_1.getArtifactZipName)(agentName));
        (0, helpers_1.createZipFromDirSync)(finalStaging, artifactPath);
        const sizeBytes = (0, helpers_1.enforceZipSizeLimitSync)(artifactPath);
        return {
            artifactPath,
            sizeBytes,
            stagingPath: finalStaging,
        };
    }
    installWithRetriesSync(projectRoot, srcDir, stagingDir, runtimeVersion, pythonPlatforms, pyprojectPath) {
        const pythonVersion = extractPythonVersion(runtimeVersion);
        for (const platform of pythonPlatforms) {
            if (!platform) {
                throw new types_1.PackagingError('No platform candidate available for dependency installation.');
            }
            (0, helpers_1.ensureDirCleanSync)(stagingDir);
            const result = (0, subprocess_1.runSubprocessCaptureSync)('uv', [
                'pip',
                'install',
                '-r',
                pyprojectPath,
                '--target',
                stagingDir,
                '--python-version',
                pythonVersion,
                '--python-platform',
                platform,
                '--only-binary',
                ':all:',
            ], { cwd: projectRoot });
            if (result.code === 0) {
                (0, helpers_1.copySourceTreeSync)(srcDir, stagingDir);
                (0, helpers_1.convertWindowsScriptsToLinuxSync)(stagingDir);
                return stagingDir;
            }
            else {
                const platformIssue = (0, uv_1.detectUnavailablePlatform)(result);
                if (platformIssue) {
                    continue;
                }
                const combined = `${result.stdout}\n${result.stderr}`.trim();
                throw new types_1.PackagingError(combined.length > 0 ? combined : `uv install failed on platform ${platform} with exit code ${result.code}`);
            }
        }
        throw new types_1.PackagingError('uv install failed for all platform candidates.');
    }
}
exports.PythonCodeZipPackagerSync = PythonCodeZipPackagerSync;
//# sourceMappingURL=python.js.map