"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ImagesInfoSaver = void 0;
const util_1 = __importDefault(require("util"));
const debug_1 = __importDefault(require("debug"));
const eventemitter2_1 = __importDefault(require("eventemitter2"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const image_size_1 = __importDefault(require("image-size"));
const lodash_1 = __importDefault(require("lodash"));
const p_queue_1 = __importDefault(require("p-queue"));
const utils_1 = require("./test-adapter/utils");
const image_cache_1 = require("./image-cache");
const constants_1 = require("./constants");
const server_utils_1 = require("./server-utils");
const common_utils_1 = require("./common-utils");
const debug = (0, debug_1.default)('html-reporter:images-info-saver');
class ImagesInfoSaver extends eventemitter2_1.default {
    constructor(options) {
        super();
        this._imageFileSaver = options.imageFileSaver;
        this._reportPath = options.reportPath;
        this._imageStore = options.imageStore;
        this._expectedPathsCache = options.expectedPathsCache;
    }
    async save(testResult, workers) {
        const testDebug = debug.extend(testResult.imageDir);
        testDebug(`Saving images of ${testResult.id}`);
        const newImagesInfos = await Promise.all(testResult.imagesInfo.map(async (imagesInfo, index) => {
            const imageDebug = testDebug.extend(index.toString());
            imageDebug.enabled && imageDebug('Handling %j', (0, utils_1.removeBufferFromImagesInfo)(imagesInfo));
            const newImagesInfo = lodash_1.default.clone(imagesInfo);
            const { stateName } = imagesInfo;
            const actions = new p_queue_1.default();
            actions.add(async () => {
                newImagesInfo.actualImg
                    = await this._saveActualImageIfNeeded(testResult, imagesInfo, stateName, { logger: imageDebug });
            });
            actions.add(async () => {
                newImagesInfo.diffImg =
                    await this._saveDiffImageIfNeeded(testResult, imagesInfo, stateName, { workers, logger: imageDebug });
            });
            actions.add(async () => {
                newImagesInfo.expectedImg =
                    await this._saveExpectedImageIfNeeded(testResult, imagesInfo, stateName, { logger: imageDebug });
            });
            await actions.onIdle();
            return lodash_1.default.omitBy(newImagesInfo, lodash_1.default.isNil);
        }));
        await this.emitAsync(constants_1.PluginEvents.TEST_SCREENSHOTS_SAVED, {
            testId: (0, common_utils_1.mkTestId)(testResult.fullName, testResult.browserId),
            attempt: testResult.attempt,
            imagesInfo: newImagesInfos
        });
        return (0, utils_1.copyAndUpdate)(testResult, { imagesInfo: newImagesInfos });
    }
    setImageFileSaver(imageFileSaver) {
        this._imageFileSaver = imageFileSaver;
    }
    async _createDiffInFile(imagesInfo, filePath, workers) {
        await (0, server_utils_1.makeDirFor)(filePath);
        const actualPath = imagesInfo.actualImg.path;
        const expectedPath = imagesInfo.expectedImg.path;
        const [currBuffer, refBuffer] = await Promise.all([
            fs_extra_1.default.readFile(actualPath),
            fs_extra_1.default.readFile(expectedPath)
        ]);
        const hash = (0, server_utils_1.createHash)(currBuffer) + (0, server_utils_1.createHash)(refBuffer);
        if (image_cache_1.cacheDiffImages.has(hash)) {
            const cachedDiffPath = image_cache_1.cacheDiffImages.get(hash);
            await fs_extra_1.default.copy(cachedDiffPath, filePath);
        }
        else {
            await workers.saveDiffTo({
                ...imagesInfo.diffOptions,
                reference: expectedPath,
                current: actualPath
            }, filePath);
            image_cache_1.cacheDiffImages.set(hash, filePath);
        }
        return { path: filePath, size: lodash_1.default.pick((0, image_size_1.default)(filePath), ['height', 'width']) };
    }
    _getReusedExpectedPath(testResult, imagesInfo) {
        if (imagesInfo.status === constants_1.UPDATED) {
            return null;
        }
        const { stateName } = imagesInfo;
        if (this._expectedPathsCache.has([testResult, stateName])) {
            return this._expectedPathsCache.get([testResult, stateName]);
        }
        const lastImageInfo = this._imageStore.getLastImageInfoFromDb(testResult, stateName);
        if (lastImageInfo && lastImageInfo.expectedImg) {
            this._expectedPathsCache.set([testResult, stateName], lastImageInfo.expectedImg.path);
            return lastImageInfo.expectedImg.path;
        }
        return null;
    }
    async _saveImage(imageData, destPath) {
        const sourceFilePath = (0, common_utils_1.isImageBufferData)(imageData) || (0, common_utils_1.isBase64Image)(imageData) ? (0, server_utils_1.getTempPath)(destPath) : imageData.path;
        if ((0, common_utils_1.isImageBufferData)(imageData)) {
            await (0, server_utils_1.makeDirFor)(sourceFilePath);
            await fs_extra_1.default.writeFile(sourceFilePath, Buffer.from(imageData.buffer));
        }
        else if ((0, common_utils_1.isBase64Image)(imageData)) {
            await (0, server_utils_1.makeDirFor)(sourceFilePath);
            await fs_extra_1.default.writeFile(sourceFilePath, Buffer.from(imageData.base64, 'base64'), 'base64');
        }
        const savedFilePath = await this._imageFileSaver.saveImg(sourceFilePath, {
            destPath,
            reportDir: this._reportPath
        });
        return savedFilePath || destPath;
    }
    async _saveActualImageIfNeeded(testResult, imagesInfo, stateName, { logger }) {
        const actualImg = imagesInfo.actualImg;
        if (!actualImg) {
            return actualImg;
        }
        const reportActualPath = (0, server_utils_1.getCurrentPath)(testResult, stateName);
        const newActualPath = await this._saveImage(actualImg, reportActualPath);
        logger(`Saved actual image from ${actualImg.path ?? '<buffer>'} to ${newActualPath}`);
        return { path: newActualPath, size: actualImg.size };
    }
    async _saveDiffImageIfNeeded(testResult, imagesInfo, stateName, { workers, logger }) {
        const shouldSaveDiff = imagesInfo.status === constants_1.TestStatus.FAIL &&
            (imagesInfo.diffImg || (imagesInfo.actualImg && imagesInfo.expectedImg));
        if (!shouldSaveDiff) {
            return;
        }
        let { diffImg } = imagesInfo;
        const reportDiffPath = (0, server_utils_1.getDiffPath)(testResult, stateName);
        if (!diffImg) {
            if (!workers) {
                throw new Error('Couldn\'t generate diff image, because workers were not passed.\n' +
                    util_1.default.format('Test result: %o\n', testResult) +
                    `Please report this error to html-reporter team: ${constants_1.NEW_ISSUE_LINK}.`);
            }
            diffImg = await this._createDiffInFile(imagesInfo, reportDiffPath, workers);
            logger(`Created new diff in file ${reportDiffPath}`);
        }
        const newDiffPath = await this._saveImage(diffImg, reportDiffPath);
        logger(`Saved diff image from ${diffImg.path ?? '<buffer>'} to ${newDiffPath}`);
        const size = lodash_1.default.pick((0, image_size_1.default)((0, common_utils_1.isImageBufferData)(diffImg) ? Buffer.from(diffImg.buffer) : diffImg.path), ['height', 'width']);
        return { path: newDiffPath, size };
    }
    async _saveExpectedImageIfNeeded(testResult, imagesInfo, stateName, { logger }) {
        if (!imagesInfo.expectedImg) {
            return;
        }
        const { expectedImg } = imagesInfo;
        const reusedExpectedPath = this._getReusedExpectedPath(testResult, imagesInfo);
        const reportDiffPath = reusedExpectedPath ?? (0, server_utils_1.getReferencePath)(testResult, stateName);
        let newExpectedPath = reportDiffPath;
        if (!reusedExpectedPath) {
            newExpectedPath = await this._saveImage(expectedImg, reportDiffPath);
            logger(`Saved expected image from ${expectedImg.path ?? '<buffer>'} to ${newExpectedPath}`);
        }
        else {
            logger(`Reused expected image from ${reusedExpectedPath}`);
        }
        return { path: newExpectedPath, size: expectedImg.size };
    }
}
exports.ImagesInfoSaver = ImagesInfoSaver;
//# sourceMappingURL=images-info-saver.js.map