/*
 * Script for minimizing big images (jpg,gif,png) when they are uploaded to GitHub and not edited otherwise.
 * Source: https://github.com/TeamNewPipe/NewPipe/blob/dev/.github/workflows/image-minimizer.js
 */
module.exports = async ({github, context}) => {
    const IGNORE_KEY = '<!-- IGNORE IMAGE MINIFY -->';
    const IGNORE_ALT_NAME_END = 'ignoreImageMinify';
    // Targeted maximum height
    const IMG_MAX_HEIGHT_PX = 400;
    // maximum width of GitHub issues/comments
    const IMG_MAX_WIDTH_PX = 800;
    // all images that have a lower aspect ratio (-> have a smaller width) than this will be minimized
    const MIN_ASPECT_RATIO = IMG_MAX_WIDTH_PX / IMG_MAX_HEIGHT_PX

    // Get the body of the image
    let initialBody = null;
    if (context.eventName == 'issue_comment') {
        initialBody = context.payload.comment.body;
    } else if (context.eventName == 'issues') {
        initialBody = context.payload.issue.body;
    } else if (context.eventName == 'pull_request') {
        initialBody = context.payload.pull_request.body;
    } else {
        console.log('Aborting: No body found');
        return;
    }
    console.log(`Found body: \n${initialBody}\n`);

    // Check if we should ignore the currently processing element
    if (initialBody.includes(IGNORE_KEY)) {
        console.log('Ignoring: Body contains IGNORE_KEY');
        return;
    }

    // Regex for finding images (simple variant) ![ALT_TEXT](https://*.githubusercontent.com/<number>/<variousHexStringsAnd->.<fileExtension>)
    const REGEX_USER_CONTENT_IMAGE_LOOKUP = /\!\[(.*)\]\((https:\/\/[-a-z0-9]+\.githubusercontent\.com\/\d+\/[-0-9a-f]{32,512}\.(jpg|gif|png))\)/gm;
    const REGEX_ASSETS_IMAGE_LOCKUP = /\!\[(.*)\]\((https:\/\/github\.com\/user-attachments\/assets\/[\-0-9a-f]{36,})\)/gm;

    // Check if we found something
    let foundSimpleImages = REGEX_USER_CONTENT_IMAGE_LOOKUP.test(initialBody)
        || REGEX_ASSETS_IMAGE_LOCKUP.test(initialBody);
    if (!foundSimpleImages) {
        console.log('Found no simple images to process');
        return;
    }

    console.log('Found at least one simple image to process');

    // Require the probe lib for getting the image dimensions
    const probe = require('probe-image-size');
    
    var wasMatchModified = false;

    // Try to find and replace the images with minimized ones
    let newBody = await replaceAsync(initialBody, REGEX_USER_CONTENT_IMAGE_LOOKUP, minimizeAsync);
    newBody = await replaceAsync(newBody, REGEX_ASSETS_IMAGE_LOCKUP, minimizeAsync);
    
    if (!wasMatchModified) {
        console.log('Nothing was modified. Skipping update');
        return;
    }

    // Update the corresponding element
    if (context.eventName == 'issue_comment') {
        console.log('Updating comment with id', context.payload.comment.id);
        await github.rest.issues.updateComment({
            comment_id: context.payload.comment.id,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: newBody
        })
    } else if (context.eventName == 'issues') {
        console.log('Updating issue', context.payload.issue.number);
        await github.rest.issues.update({
            issue_number: context.payload.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: newBody
        });
    } else if (context.eventName == 'pull_request') {
        console.log('Updating pull request', context.payload.pull_request.number);
        await github.rest.pulls.update({
            pull_number: context.payload.pull_request.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: newBody
        });
    }

    // Async replace function from https://stackoverflow.com/a/48032528
    async function replaceAsync(str, regex, asyncFn) {
        const promises = [];
        str.replace(regex, (match, ...args) => {
            const promise = asyncFn(match, ...args);
            promises.push(promise);
        });
        const data = await Promise.all(promises);
        return str.replace(regex, () => data.shift());
    }

    async function minimizeAsync(match, g1, g2) {
            console.log(`Found match '${match}'`);

            if (g1.endsWith(IGNORE_ALT_NAME_END)) {
                console.log(`Ignoring match '${match}': IGNORE_ALT_NAME_END`);
                return match;
            }

            let probeAspectRatio = 0;
            let shouldModify = false;
            try {
                console.log(`Probing ${g2}`);
                let probeResult = await probe(g2);
                if (probeResult == null) {
                    throw 'No probeResult';
                }
                if (probeResult.hUnits != 'px') {
                    throw `Unexpected probeResult.hUnits (expected px but got ${probeResult.hUnits})`;
                }
                if (probeResult.height <= 0) {
                    throw `Unexpected probeResult.height (height is invalid: ${probeResult.height})`;
                }
                if (probeResult.wUnits != 'px') {
                    throw `Unexpected probeResult.wUnits (expected px but got ${probeResult.wUnits})`;
                }
                if (probeResult.width <= 0) {
                    throw `Unexpected probeResult.width (width is invalid: ${probeResult.width})`;
                }
                console.log(`Probing resulted in ${probeResult.width}x${probeResult.height}px`);

                probeAspectRatio = probeResult.width / probeResult.height;
                shouldModify = probeResult.height > IMG_MAX_HEIGHT_PX && probeAspectRatio < MIN_ASPECT_RATIO;
            } catch(e) {
                console.log('Probing failed:', e);
                // Immediately abort
                return match;
            }

            if (shouldModify) {
                wasMatchModified = true;
                console.log(`Modifying match '${match}'`);
                return `<img alt="${g1}" src="${g2}" width=${Math.min(600, Math.floor(IMG_MAX_HEIGHT_PX * probeAspectRatio))} />`;
            }

            console.log(`Match '${match}' is ok/will not be modified`);
            return match;
        }
}
