"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DownloadProperty = exports.FAIL_STATUS = exports.SUCCESS_STATUS = exports.DOWNLOAD_COMPLETE_POLLING_INTERVAL_MILLISECONDS = void 0;
exports.toSnakeCasePropertyName = toSnakeCasePropertyName;
exports.removeFileAutoNumbering = removeFileAutoNumbering;
exports.fileMimeTypeChecker = fileMimeTypeChecker;
exports.fileNameChecker = fileNameChecker;
exports.fileSizeChecker = fileSizeChecker;
exports.humanizeDownloadBaseStep = humanizeDownloadBaseStep;
exports.downloadBaseStepToAIString = downloadBaseStepToAIString;
const mablscriptFind_1 = require("@mablhq/test-model/mablscriptFind");
const type_utils_1 = require("../../type-utils");
const variable_names_util_1 = require("../../variable-names-util");
const BaseStep_1 = __importDefault(require("./BaseStep"));
const DownloadStep_1 = require("@mablhq/test-model/steps/DownloadStep");
const fileNameMatchUtil_1 = require("../../../../util/fileNameMatchUtil");
const yaml_utils_1 = require("../yaml-utils");
exports.DOWNLOAD_COMPLETE_POLLING_INTERVAL_MILLISECONDS = 200;
const MISSING_DATA_STATUS = 'MISSING DATA';
exports.SUCCESS_STATUS = 'OK';
exports.FAIL_STATUS = 'FAIL';
var DownloadProperty;
(function (DownloadProperty) {
    DownloadProperty["FileMd5"] = "file_md5";
    DownloadProperty["FileName"] = "file_name";
    DownloadProperty["FilePathLocal"] = "file_path_local";
    DownloadProperty["FileSha256"] = "file_sha256";
    DownloadProperty["MimeType"] = "file_mime_type";
    DownloadProperty["SizeBytes"] = "file_size_bytes";
    DownloadProperty["SizeBytesMax"] = "file_size_bytes_max";
    DownloadProperty["SizeBytesMin"] = "file_size_bytes_min";
})(DownloadProperty || (exports.DownloadProperty = DownloadProperty = {}));
const PROPERTY_NAME_MAPPING = {
    aiPrompt: 'ai_prompt',
    fileMd5: 'file_md5',
    fileName: 'file_name',
    filePathLocal: 'file_path_local',
    fileSha256: 'file_sha256',
    mimeType: 'file_mime_type',
    sizeBytes: 'file_size_bytes',
    sizeBytesMin: 'file_size_bytes_min',
    sizeBytesMax: 'file_size_bytes_max',
};
function toSnakeCasePropertyName(camelCaseName) {
    return PROPERTY_NAME_MAPPING[camelCaseName] || camelCaseName;
}
class DownloadBaseStep extends BaseStep_1.default {
    static assertionsTemplate() {
        return [
            {
                name: DownloadProperty.FileName,
                type: 'string',
                desc: 'File name',
                init: 'example.txt',
            },
            {
                name: DownloadProperty.MimeType,
                type: 'string',
                desc: 'File type',
                init: 'example/type',
            },
            {
                name: DownloadProperty.SizeBytes,
                type: 'number',
                desc: 'Exact file size (bytes)',
                init: '0',
            },
            {
                name: DownloadProperty.SizeBytesMin,
                type: 'number',
                desc: 'Min file size (bytes)',
                init: '0',
            },
            {
                name: DownloadProperty.SizeBytesMax,
                type: 'number',
                desc: 'Max file size (bytes)',
                init: '0',
            },
        ];
    }
    constructor(values) {
        super(values.stepType);
        this.detectedProps = values.detectedProperties || { download: true };
        this.assertions =
            values.assertions || DownloadBaseStep.assertionsTemplate();
        this.awaitSymbol = values.awaitSymbol;
        this.stepSymbol = 'assert_download';
        this.awaitParams = values.awaitParameters || {};
        this.aiPrompt = values.aiPrompt;
        this.fileNameMatchMode = values.fileNameMatchMode ?? 'equals';
        this.options = {
            editable: true,
            executionContext: 'background',
            timeoutMilliseconds: 30000,
        };
        const foundIndex = this.assertions.findIndex((assertion) => assertion.name === DownloadProperty.FileName);
        if (foundIndex !== -1) {
            const fileNameAssertion = this.assertions[foundIndex];
            const expectations = this.expectations();
            const fileName = this.detectedProps?.file_name;
            if (expectations?.length === 0 &&
                fileNameAssertion?.expected === undefined &&
                fileName) {
                const cleanFilename = (0, mablscriptFind_1.escapeMablscriptString)(DownloadBaseStep.downloadPathToFilename(fileName));
                if (cleanFilename) {
                    fileNameAssertion.expected = cleanFilename;
                    fileNameAssertion.escaped = true;
                }
            }
        }
    }
    generateMablStep() {
        const descriptor = this.buildDescriptor();
        const stepName = this.getMablStepName();
        const mablStep = new DownloadStep_1.DownloadStep(stepName, descriptor);
        if (this.id) {
            mablStep.setStepId(this.id);
        }
        if (this.annotations) {
            mablStep.setAnnotation(this.annotations);
        }
        if (this.disabled) {
            mablStep.setDisabled(true);
        }
        return mablStep;
    }
    getMablStepName() {
        return DownloadStep_1.DownloadStep.stepName;
    }
    buildDescriptor() {
        return this.buildBaseDescriptor();
    }
    buildBaseDescriptor() {
        const getAssertionValue = (propertyName) => {
            const assertion = this.assertions?.find((a) => a.name === propertyName);
            return assertion?.expected;
        };
        return {
            fileName: getAssertionValue(DownloadProperty.FileName),
            mimeType: getAssertionValue(DownloadProperty.MimeType),
            sizeBytes: getAssertionValue(DownloadProperty.SizeBytes),
            sizeBytesMin: getAssertionValue(DownloadProperty.SizeBytesMin),
            sizeBytesMax: getAssertionValue(DownloadProperty.SizeBytesMax),
            fileMd5: getAssertionValue(DownloadProperty.FileMd5),
            fileSha256: getAssertionValue(DownloadProperty.FileSha256),
            aiPrompt: this.aiPrompt,
            fileNameMatchMode: this.fileNameMatchMode,
            detected: {
                file_mime_type: this.detectedProps?.file_mime_type,
                file_name: this.detectedProps?.file_name,
                file_size_bytes: this.detectedProps?.file_size_bytes,
            },
        };
    }
    generateMablscript() {
        return this.generateAwaitDownload() + '.' + this.generateAssertDownload();
    }
    generateAwaitDownload() {
        const stepParams = this.awaitParams;
        const stepParamString = Object.keys(stepParams).length === 0
            ? ''
            : (0, mablscriptFind_1.buildStepArgumentString)({ params: { ...stepParams }, legacy: true });
        return `${this.awaitSymbol}(${stepParamString})`;
    }
    generateAssertDownload() {
        const stepParams = {};
        if (this.aiPrompt) {
            stepParams.ai_prompt = this.aiPrompt;
        }
        stepParams.file_name_match_mode = this.fileNameMatchMode;
        this.assertions
            .filter((assertion) => assertion.expected)
            .forEach((assertion) => {
            stepParams[assertion.name] =
                assertion.type === 'number' && typeof assertion.expected === 'string'
                    ? parseInt(assertion?.expected)
                    : assertion.expected;
        });
        const stepParamString = (0, mablscriptFind_1.buildStepArgumentString)({
            params: { ...stepParams },
            legacy: true,
        });
        return `${this.stepSymbol}(${stepParamString})`;
    }
    expectations() {
        return expectations(this);
    }
    static downloadPathToFilename(fullPath) {
        return fullPath.split(/[\\/]/).pop();
    }
    static cleanFilenameFromPath(filePath) {
        return removeFileAutoNumbering(DownloadBaseStep.downloadPathToFilename(filePath) ?? '');
    }
    inputVariables() {
        const variableNames = [];
        this.expectations()
            .map((assertion) => `${assertion.expected}`)
            .map(variable_names_util_1.extractVariableNamesFromPattern)
            .forEach((names) => variableNames.push(...names));
        return (0, type_utils_1.distinct)(variableNames);
    }
}
exports.default = DownloadBaseStep;
function generateFail(expected, actual, message) {
    return {
        status: exports.FAIL_STATUS,
        expected,
        actual,
        errorMessage: message,
    };
}
function removeFileAutoNumbering(filename) {
    const autoNumberedFilenameRegex = /^(.+\S|.+)\s?(\(\d+\))(.+)$/;
    if (autoNumberedFilenameRegex.test(filename)) {
        const matches = filename.match(autoNumberedFilenameRegex);
        if (!matches) {
            throw new Error('Could not remove file auto numbering, no filename match');
        }
        const filePart = matches[1];
        const extensionPart = matches[3];
        return filePart + extensionPart;
    }
    return filename;
}
function homogenizeMimeType(mimeType) {
    return mimeType?.toLowerCase();
}
function fileMimeTypeChecker(assertion, downloadData) {
    if (downloadData?.[DownloadProperty.MimeType]) {
        const fileMimeType = homogenizeMimeType(downloadData[DownloadProperty.MimeType]);
        const expected = homogenizeMimeType(assertion.expected);
        return fileMimeType === expected
            ? exports.SUCCESS_STATUS
            : generateFail(expected, fileMimeType, 'Unexpected file MIME type');
    }
    return MISSING_DATA_STATUS;
}
function fileNameChecker(assertion, downloadData, fileNameMatchMode) {
    if (downloadData?.[DownloadProperty.FileName]) {
        const filename = removeFileAutoNumbering(DownloadBaseStep.downloadPathToFilename(downloadData[DownloadProperty.FileName]) ?? '');
        const expected = removeFileAutoNumbering(assertion.expected);
        if (expected === '') {
            return exports.SUCCESS_STATUS;
        }
        return (0, fileNameMatchUtil_1.evaluateFileNameMatch)(filename, expected, fileNameMatchMode)
            ? exports.SUCCESS_STATUS
            : generateFail(expected, filename, 'Unexpected file name');
    }
    return MISSING_DATA_STATUS;
}
function fileSizeChecker(assertion, downloadData) {
    const sizeProperties = [
        DownloadProperty.SizeBytesMin,
        DownloadProperty.SizeBytesMax,
        DownloadProperty.SizeBytes,
    ];
    const expectedFileSizeBytes = assertion.expected;
    const actualFileSizeBytes = downloadData[DownloadProperty.SizeBytes];
    const discrepancies = sizeProperties
        .filter((key) => assertion && assertion.name === key)
        .map((downloadKey) => {
        switch (downloadKey) {
            case DownloadProperty.SizeBytesMin:
                return actualFileSizeBytes >= expectedFileSizeBytes
                    ? exports.SUCCESS_STATUS
                    : generateFail(expectedFileSizeBytes, actualFileSizeBytes, 'Below min file size');
            case DownloadProperty.SizeBytesMax:
                return actualFileSizeBytes <= expectedFileSizeBytes
                    ? exports.SUCCESS_STATUS
                    : generateFail(expectedFileSizeBytes, actualFileSizeBytes, 'Above max file size');
            case DownloadProperty.SizeBytes:
                return expectedFileSizeBytes === actualFileSizeBytes
                    ? exports.SUCCESS_STATUS
                    : generateFail(expectedFileSizeBytes, actualFileSizeBytes, 'Unexpected file size');
            default:
                return;
        }
    });
    return discrepancies ? discrepancies[0] : MISSING_DATA_STATUS;
}
function humanizeDownloadBaseStep(step) {
    const download = step.detectedProps || {};
    let cleanFilename = '';
    if (download[DownloadProperty.FileName]) {
        cleanFilename = `"${(0, mablscriptFind_1.escapeMablscriptString)(DownloadBaseStep.downloadPathToFilename(download[DownloadProperty.FileName] ?? ''))}"`;
    }
    const generatedExpectations = expectations(step);
    if (generatedExpectations.length === 0) {
        return 'Assert file downloaded';
    }
    return generatedExpectations
        .map((assertion) => {
        let assertionTarget = assertion.expected;
        if (assertion.type === 'string') {
            assertionTarget = assertion.escaped
                ? `"${assertion.expected}"`
                : `"${(0, mablscriptFind_1.escapeMablscriptString)(assertion.expected)}"`;
        }
        if (assertion.name === DownloadProperty.FileName &&
            (!assertion.expected || !cleanFilename)) {
            return 'Assert file downloaded';
        }
        const matchVerb = assertion.name === DownloadProperty.FileName &&
            step.fileNameMatchMode !== 'equals'
            ? step.fileNameMatchMode.replace('_', ' ')
            : 'equals';
        return `Assert ${assertion.desc} ${matchVerb} ${assertionTarget} for download ${cleanFilename}`;
    })
        .join('\t');
}
function expectations(step) {
    return (step.assertions || []).filter((assertion) => assertion?.expected !== undefined);
}
function downloadBaseStepToAIString(step) {
    const result = {
        stepType: step.stepType,
    };
    const activeAssertions = step.expectations();
    if (activeAssertions.length > 0) {
        result.assertions = activeAssertions.map((a) => ({
            property: a.name,
            expected: a.expected,
            ...(a.name === DownloadProperty.FileName &&
                step.fileNameMatchMode !== 'equals'
                ? { match_mode: step.fileNameMatchMode }
                : {}),
        }));
    }
    if (step.aiPrompt?.userPrompt) {
        result.ai_prompt = step.aiPrompt.userPrompt;
    }
    return (0, yaml_utils_1.toYaml)(result);
}
