import { copyFile, copyFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
import { getOutputDirectory } from './config.js';

const loadInstantGamesStr = `/* needle: injected to initialize facebook instant games */
    let __progress = 0;
    function onNeedleEngineInstantGamesLoadingProgress(_ctx, evt) {
        FBInstant.setLoadingProgress(evt.detail.totalProgress01 * 100);
    }
    document.addEventListener("DOMContentLoaded", function () {
        FBInstant.initializeAsync().then(function () { FBInstant.startGameAsync() });
    });
`

// https://regex101.com/r/4ApFD9/1
function insertCallToOnProgress(html) {
    const match = /(?<component><needle-engine.*?.*?>)/g.exec(html);
    if (!match) {
        log("!!! Could not find needle-engine component in index.html");
        return html;
    }
    const progressEvtSignature = "progress=\"onNeedleEngineInstantGamesLoadingProgress\"";
    const component = match.groups.component;
    if (component.includes("progress")) return html;
    log("Inserting progress callback event into needle-engine component")
    const newComponent = component.replace(">", ` ${progressEvtSignature}>`);
    return html.replace(component, newComponent);
}

function log(...any) {
    console.log("[needle facebook instant games] ", ...any);
}

/**
 * @param {"build" | "serve"} command
 * @param {import('../types/needleConfig').needleMeta | null | undefined} config
 * @param {import('../types').userSettings} userSettings
 * @returns {import('vite').Plugin | undefined}
 */
export function needleFacebookInstantGames(command, config, userSettings) {

    if (userSettings.noFacebookInstantGames === true) return;

    // If the config is not present it means that we don't want to use fb instant games
    if (!config || !(userSettings.facebookInstantGames || config.facebookInstantGames)) return;

    /** @type {import('../types').userSettings["facebookInstantGames"]} */
    const facebookInstantGamesConfig = { ...config.facebookInstantGames, ...userSettings.facebookInstantGames };

    log("Setup Facebook Instant Games", facebookInstantGamesConfig);


    // https://developers.facebook.com/docs/games/build/instant-games/reference/bundle-config
    let bundleConfig = {}
    const bundleConfigPath = process.cwd() + "/fbapp-config.json";
    if (existsSync(bundleConfigPath)) {
        log(`Found facebook bundle config exists at "${bundleConfigPath}" ...`);
        bundleConfig = JSON.parse(readFileSync(bundleConfigPath, 'utf8'));
    }
    else {
        log(`No facebook bundle config exists at "${bundleConfigPath}" - will generate one now`);
    }

    // Make sure the bundle has the required arguments:
    if (!bundleConfig.instant_games) bundleConfig.instant_games = {}
    const gamesConfig = bundleConfig.instant_games;
    if (!gamesConfig.orientation) gamesConfig.orientation = "PORTRAIT";
    if (!gamesConfig.override_web_orientation) gamesConfig.override_web_orientation = "LANDSCAPE";
    if (!gamesConfig.navigation_menu_version) gamesConfig.navigation_menu_version = "NAV_BAR";

    const outputDir = getOutputDirectory();

    return {
        name: 'needle:facebook-instant-games',
        transformIndexHtml: {
            order: 'post',
            handler(html, _ctx) {
                // post transform so we want to linebreak after the vite logs
                console.log("\n");

                writeFileSync(outputDir + "/fbapp-config.json", JSON.stringify(bundleConfig), 'utf8');

                html = insertCallToOnProgress(html);

                return {
                    html,
                    tags: [
                        {
                            tag: 'script',
                            attrs: {
                                src: 'https://connect.facebook.net/en_US/fbinstant.6.3.js',
                                async: true,
                            },
                            injectTo: 'head',
                        },
                        {
                            tag: 'script',
                            children: loadInstantGamesStr,
                            injectTo: 'body',
                        },
                    ]
                }
            }
        },
    }
}