"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeZipEntryPath = normalizeZipEntryPath;
exports.validateCustomAIBundle = validateCustomAIBundle;
const adm_zip_1 = __importDefault(require("adm-zip"));
const validateZipArchiveSecurity_1 = require("./validateZipArchiveSecurity");
const validateJsBundleDependencies_1 = require("./validateJsBundleDependencies");
function normalizeZipEntryPath(entryPath) {
    return entryPath.replace(/\\/g, '/').replace(/^\/+/, '');
}
function extensionMatchesLanguage(language, filePath) {
    const lower = filePath.toLowerCase();
    if (language === 'python') {
        return lower.endsWith('.py');
    }
    if (language === 'go') {
        return lower.endsWith('.go');
    }
    return (lower.endsWith('.js') ||
        lower.endsWith('.mjs') ||
        lower.endsWith('.cjs') ||
        lower.endsWith('.ts'));
}
function zipHasLanguageArtifacts(language, entryPaths) {
    return entryPaths.some((entry) => extensionMatchesLanguage(language, entry));
}
function resolveApplicationPath(language, applicationPath) {
    const normalized = normalizeZipEntryPath(applicationPath.trim());
    if (!normalized) {
        throw new Error('Application path is required');
    }
    if (normalized.includes(':')) {
        throw new Error('Application path must be a file path only (no colon suffix)');
    }
    if (!extensionMatchesLanguage(language, normalized)) {
        if (language === 'python') {
            throw new Error('Application path must end with .py for Python');
        }
        if (language === 'go') {
            throw new Error('Application path must end with .go for Go');
        }
        throw new Error('Application path must end with .js, .mjs, .cjs, or .ts for Javascript');
    }
    return normalized;
}
function fileExistsInZip(entryPaths, applicationPath) {
    const normalizedTarget = normalizeZipEntryPath(applicationPath);
    return entryPaths.some((entry) => {
        const normalizedEntry = normalizeZipEntryPath(entry);
        return (
        // Require an exact zip entry path match. This prevents accepting a bare filename
        // (e.g. handler.py) when the file actually lives under a subdirectory (e.g. src/handler.py).
        normalizedEntry === normalizedTarget);
    });
}
function detectCallApiFunction(source, language) {
    if (language === 'python') {
        return /\bdef\s+call_api\s*\(/.test(source) || /\basync\s+def\s+call_api\s*\(/.test(source);
    }
    if (language === 'go') {
        return /\bfunc\s+(CallApi|call_api)\s*\(/.test(source);
    }
    return (/\bfunction\s+callApi\s*\(/.test(source) ||
        /\basync\s+function\s+callApi\s*\(/.test(source) ||
        /\bcallApi\s*=\s*async\s*\(/.test(source) ||
        /\bcallApi\s*=\s*\(/.test(source) ||
        /\bexports\.callApi\b/.test(source) ||
        /\bmodule\.exports\.callApi\b/.test(source) ||
        /\bexport\s+async\s+function\s+callApi\b/.test(source) ||
        /\bexport\s+function\s+callApi\b/.test(source));
}
function validateCustomAIBundle(params) {
    const errors = [];
    const { zipBuffer, language } = params;
    let resolvedPath;
    try {
        resolvedPath = resolveApplicationPath(language, params.applicationPath);
    }
    catch (error) {
        return {
            valid: false,
            errors: [error instanceof Error ? error.message : String(error)],
        };
    }
    let zip;
    try {
        zip = new adm_zip_1.default(zipBuffer);
    }
    catch {
        return { valid: false, errors: ['Invalid or corrupted zip file'] };
    }
    const archiveErrors = (0, validateZipArchiveSecurity_1.validateZipArchiveSecurity)(zip, zipBuffer.length);
    if (archiveErrors.length > 0) {
        return { valid: false, errors: archiveErrors };
    }
    const entries = zip.getEntries().filter((entry) => !entry.isDirectory);
    const entryPaths = entries.map((entry) => normalizeZipEntryPath(entry.entryName));
    if (!zipHasLanguageArtifacts(language, entryPaths)) {
        const extHint = language === 'python' ? '.py' : language === 'go' ? '.go' : '.js/.mjs/.cjs/.ts';
        errors.push(`Zip must contain at least one ${language} source file (${extHint})`);
    }
    if (fileExistsInZip(entryPaths, resolvedPath)) {
        const targetEntry = entries.find((entry) => {
            const normalizedEntry = normalizeZipEntryPath(entry.entryName);
            return (normalizedEntry === resolvedPath);
        });
        if (targetEntry) {
            const source = targetEntry.getData().toString('utf8');
            if (!detectCallApiFunction(source, language)) {
                const fnName = language === 'js' ? 'callApi' : 'call_api';
                errors.push(`Provider file must define a ${fnName} function`);
            }
        }
    }
    else {
        errors.push(`Application path "${resolvedPath}" was not found in the zip`);
    }
    if (language === 'js') {
        const jsDepError = (0, validateJsBundleDependencies_1.validateJavaScriptBundleDependencies)(entryPaths, entries);
        if (jsDepError) {
            errors.push(jsDepError);
        }
    }
    return { valid: errors.length === 0, errors };
}
//# sourceMappingURL=validateCustomAIBundle.js.map