import { BUILD_TIME, GENERATOR, PUBLIC_KEY, VERSION } from "./engine_constants.js";
import { ContextEvent, ContextRegistry } from "./engine_context_registry.js";
import { Context } from "./engine_setup.js";
import type { IContext } from "./engine_types.js";
import { getParam } from "./engine_utils.js";

const debug = getParam("debuglicense");

// This is modified by a bundler (e.g. vite)
// Do not edit manually
let NEEDLE_ENGINE_LICENSE_TYPE: string = "basic";
if (debug) 
    console.log("License Type: " + NEEDLE_ENGINE_LICENSE_TYPE)

/** @internal */
export function hasProLicense() {
    switch (NEEDLE_ENGINE_LICENSE_TYPE) {
        case "pro":
        case "enterprise":
            return true;
    };
    return false;
}

/** @internal */
export function hasIndieLicense() {
    switch (NEEDLE_ENGINE_LICENSE_TYPE) {
        case "indie":
            return true;
    }
    return false;
}

/** @internal */
export function hasCommercialLicense() {
    return hasProLicense() || hasIndieLicense();
}

const _licenseCheckResultChangedCallbacks: ((result: boolean) => void)[] = [];

/** @internal */
export function onLicenseCheckResultChanged(cb: (result: boolean) => void) {
    if (hasProLicense() || hasIndieLicense())
        return cb(true);
    _licenseCheckResultChangedCallbacks.push(cb);
}

function invokeLicenseCheckResultChanged(result: boolean) {
    for (const cb of _licenseCheckResultChangedCallbacks) {
        try {
            cb(result);
        }
        catch {
            // ignore
        }
    }
}

ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
    showLicenseInfo(evt.context);
    sendUsageMessageToAnalyticsBackend();
    handleForbidden(evt.context);
});

export let runtimeLicenseCheckPromise: Promise<void> | undefined = undefined;
let applicationIsForbidden = false;
let applicationForbiddenText = "";
async function checkLicense() {
    // Only perform the runtime license check once
    if (runtimeLicenseCheckPromise) return runtimeLicenseCheckPromise;
    if (NEEDLE_ENGINE_LICENSE_TYPE === "basic") {
        try {
            const licenseUrl = "https://engine.needle.tools/licensing/check?location=" + encodeURIComponent(window.location.href) + "&version=" + VERSION + "&generator=" + encodeURIComponent(GENERATOR);
            const res = await fetch(licenseUrl, {
                method: "GET",
            }).catch(_err => {
                if (debug) console.error("License check failed", _err);
                return undefined;
            });
            if (res?.status === 200) {
                applicationIsForbidden = false;
                if (debug) console.log("License check succeeded");
                NEEDLE_ENGINE_LICENSE_TYPE = "pro";
                invokeLicenseCheckResultChanged(true);
            }
            else if (res?.status === 403) {
                applicationIsForbidden = true;
                applicationForbiddenText = await res.text();
            }
            else {
                if (debug) console.log("License check failed with status " + res?.status);
            }
        }
        catch (err) {
            if (debug) console.error("License check failed", err);
        }
    }
    else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" + NEEDLE_ENGINE_LICENSE_TYPE + "\"");
}
runtimeLicenseCheckPromise = checkLicense();

async function handleForbidden(ctx: IContext) {
    function createForbiddenElement() {
        const div = document.createElement("div");
        div.className = "needle-forbidden";
        div.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        pointer-events: all;
        zIndex: 2147483647;
        line-height: 1.5;
        backdrop-filter: blur(15px);
        -webkit-backdrop-filter: blur(15px);
        `;
        const expectedStyle = div.style.cssText;

        const text = document.createElement("div");
        div.appendChild(text);
        text.style.cssText = `
        position: absolute;
        left: 0;
        right: 0;
        top:0;
        bottom: 0;
        padding: 10%;
        color: white;
        font-size: 20px;
        font-family: sans-serif;
        text-align: center;
        pointer-events: all;
        display: flex;
        justify-content: center;
        align-items: center;
        background-color: rgba(0,0,0,.3);
        text-shadow: 0 0 2px black;
        `;
        const expectedTextStyle = text.style.cssText;
        const forbiddenText = applicationForbiddenText?.length > 1 ? applicationForbiddenText : "This web application has been paused.<br/>You might be in violation of the Needle Engine terms of use.<br/>Please contact the Needle support if you think this is a mistake.";
        text.innerHTML = forbiddenText;
        setInterval(() => {
            if (text.innerHTML !== forbiddenText) text.innerHTML = forbiddenText;
            if (text.parentNode !== div) div.appendChild(text);
            if (div.style.cssText !== expectedStyle) div.style.cssText = expectedStyle;
            if (text.style.cssText !== expectedTextStyle) text.style.cssText = expectedTextStyle;
        }, 500)
        return div;
    }
    let forbiddenElement = createForbiddenElement();
    const expectedCSS = forbiddenElement.style.cssText;
    setInterval(() => {
        if (applicationIsForbidden === true) {
            if (forbiddenElement.style.cssText !== expectedCSS) forbiddenElement = createForbiddenElement();
            if (ctx.domElement.shadowRoot) {
                if (forbiddenElement.parentNode !== ctx.domElement.shadowRoot)
                    ctx.domElement.shadowRoot?.appendChild(forbiddenElement);
            }
            else if (forbiddenElement.parentNode != document.body) {
                document.body.appendChild(forbiddenElement);
            }
        }
    }, 500)
}

async function showLicenseInfo(ctx: IContext) {
    try {
        if (hasCommercialLicense() !== true) return onNonCommercialVersionDetected(ctx);
    }
    catch (err) {
        if (debug) console.log("License check failed", err)
        return onNonCommercialVersionDetected(ctx)
    }
    if (debug) onNonCommercialVersionDetected(ctx)
}



async function onNonCommercialVersionDetected(ctx: IContext) {
    // if the engine loads faster than the license check, we need to capture the ready event here
    let isReady = false;
    ctx.domElement.addEventListener("ready", () => isReady = true);

    await runtimeLicenseCheckPromise?.catch(() => { });
    if (hasCommercialLicense()) return;
    logNonCommercialUse();

    // check if the engine is already ready (meaning has finished loading)
    // if (isReady) {
    //     insertNonCommercialUseHint(ctx);
    // }
    // else {
    //     ctx.domElement.addEventListener("ready", () => {
    //         insertNonCommercialUseHint(ctx);
    //     });
    // }
}

// const licenseElementIdentifier = "needle-license-element";
// const licenseDuration = 10000;
// const licenseDelay = 1200;
// function insertNonCommercialUseHint(ctx: IContext) {

//     return;
//     const banner = LicenseBanner.create();
//     ctx.domElement.shadowRoot?.appendChild(banner);
//     let bannerStyle = `
//         position: absolute;
//         bottom: 20px;
//         right: 20px;
//         opacity: 0;
//         transform: translateY(10px);
//         transition: all .5s ease-in-out 1s;
//         pointer: cursor;
//     `;
//     banner.style.cssText = bannerStyle;
//     let expectedBannerStyle = banner.style.cssText;
//     setTimeout(() => {
//         bannerStyle = bannerStyle.replace("opacity: 0", "opacity: 1");
//         bannerStyle = bannerStyle.replace("transform: translateY(10px)", "transform: translateY(0)");
//         banner.style.cssText = bannerStyle;
//         expectedBannerStyle = banner.style.cssText;
//     }, 100);

//     // ensure the banner is always visible
//     const interval = setInterval(() => {
//         const parent = ctx.domElement.shadowRoot || ctx.domElement;
//         if (banner.parentNode !== parent) {
//             parent.appendChild(banner);
//         }
//         if (expectedBannerStyle != banner.style.cssText) {
//             banner.style.cssText = bannerStyle;
//             expectedBannerStyle = banner.style.cssText;
//             showBalloonError("This website violates the Needle Engine License! Please contact hi@needle.tools");
//         }
//     }, 1000);

//     if (hasIndieLicense()) {
//         const removeDelay = licenseDuration + licenseDelay;
//         setTimeout(() => {
//             clearInterval(interval);
//             banner?.remove();
//             // show the logo every x minutes
//             const intervalInMinutes = 5;
//             setTimeout(() => {
//                 if (ctx.domElement.parentNode)
//                     insertNonCommercialUseHint(ctx);
//             }, 1000 * 60 * intervalInMinutes)
//         }, removeDelay);
//     }

// }
let lastLogTime = 0;
async function logNonCommercialUse(_logo?: string) {
    const now = Date.now();
    if (now - lastLogTime < 2000) return;
    lastLogTime = now;
    const logo = "data:image/webp;base64,UklGRrABAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSKEAAAARN6CmbSM4WR7vdARON11EBDq3fLiNbVtVzpMCPlKAEzsx0Y/x+Ovuv4dn0EFE/ydAvz6YggXzgh5sVgXM/zOC/4sii7qgGvB5N7hmuQYwkvazWAu1JPW41FXSHq6pnaQWvqYH18Fc0j1hO/BFTtIeSBlJi5w6qIIO7IOrwhFsB2Yxukif0FTRLpXswHR8MxbslKe9VZsn/Ub5C7YFOpqSTABWUDgg6AAAAFAGAJ0BKiAAIAA+7VyoTqmkpCI3+qgBMB2JbACdMt69DwMIQBLhkTO6XwY00UEDK6cNIDnuNibPf0EgAP7Y1myuiQHLDsF/0h5unrGh6WAbv7aegg2ZMd3uRKfT/3SJztcaujYfTvMXspfCTmYcoO6a+vhC3ss4M8uM58t4siiu59I4aOl59e9Sr6xoxYlHf2v+NnBNpJYeJf8jABQAId/PXuBkLEFkiCucgSGEcfhvajql/j3reCGl0M5/9gQWy7ayNPs+wlvIxFnNfSlfuND4CZOCyxOHhRqOmHN4ULHo3tCSrUNvgAA=";
    const style = `
        position: relative;
        display: block;
        font-size: 18px;
        background-size: 20px;
        background-position: 10px 5px;
        background-repeat:no-repeat;
        background-image:url('${logo}');
        background-max-size: 40px;
        margin-bottom: 5px;
        margin-top: .3em;
        margin-bottom: .5em;
        padding: .2em;
        padding-left: 25px;
        border-radius: .5em;
        border: 2px solid rgba(160,160,160,.3);
    `;
    // url must contain https for firefox to make it clickable
    const version = VERSION;
    const licenseText = `Needle Engine — No license active, commercial use is not allowed. Visit https://needle.tools/pricing for more information and licensing options! v${version}`;
    if (Context.Current?.xr) {
        console.log(licenseText);
    }
    else {
        console.log("%c " + licenseText, style);
    }
}


async function sendUsageMessageToAnalyticsBackend() {
    // We can't send beacons from cross-origin isolated pages
    if (window.crossOriginIsolated) return;

    try {
        const analyticsUrl = "https://needle-engine-analytics-v2-r26roub2hq-lz.a.run.app";
        if (analyticsUrl) {
            if (debug) console.log("Analytics backend url", analyticsUrl);

            // analyticsUrl = "http://localhost:3000/";

            // current url without query parameters
            const currentUrl = window.location.href.split("?")[0];

            let endpoint = "api/v2/new/request";
            if (!analyticsUrl.endsWith("/")) endpoint = "/" + endpoint;
            const license = NEEDLE_ENGINE_LICENSE_TYPE;
            const finalUrl = `${analyticsUrl}${endpoint}`;
            if (debug) console.log("Sending non-commercial usage message to analytics backend", finalUrl);

            const beaconData = {
                license,
                url: currentUrl,
                hostname: window.location.hostname,
                pathname: window.location.pathname,
                // search: window.location.search,
                // hash: window.location.hash,
                version: VERSION,
                generator: GENERATOR,
                build_time: BUILD_TIME,
                public_key: PUBLIC_KEY,
            };
            const res = navigator.sendBeacon?.(finalUrl, JSON.stringify(beaconData));
            if (debug) console.log("Send beacon result", res);
        }
    }
    catch (err) {
        if (debug)
            console.log("Failed to send non-commercial usage message to analytics backend", err);
    }
}