'use strict';

import { Controller } from "@hotwired/stimulus";
import * as altcha from "altcha";

export default class extends Controller {
    static values = {
        challengeUrl: String,
        inputId: String,
        testMode: { type: Boolean, default: false },
    };

    getStrings() {
        const locale = document.documentElement.lang?.toLowerCase() ?? "fr";

        if (locale.startsWith("en")) {
            return {
                error: "Verification failed. Please try again later.",
                expired: "Verification expired. Please try again.",
                footer: 'Protected by <a href="https://altcha.org/" target="_blank" aria-label="Visit Altcha.org">ALTCHA</a>',
                label: "I am not a robot",
                verified: "Verified",
                verifying: "Verifying...",
                waitAlert: "Verifying... please wait.",
            };
        }

        return {
            error: "Échec de la vérification. Réessayez plus tard.",
            expired: "Vérification expirée. Réessayez.",
            footer: 'Protégé par <a href="https://altcha.org/" target="_blank" aria-label="Visitez Altcha.org">ALTCHA</a>',
            label: "Je ne suis pas un robot",
            verified: "Vérifié",
            verifying: "Vérification en cours...",
            waitAlert: "Vérification en cours... veuillez patienter.",
        };
    }

    async initialize() {
        await altcha;
    }

    connect() {
        this.syncPayloadBound = () => {
            this.scheduleSync();
        };
        this.submitSyncBound = () => {
            this.scheduleSync(true);
        };
        this.formElement = this.element.closest("form");

        this.element.addEventListener("statechange", this.syncPayloadBound);
        this.element.addEventListener("verified", this.syncPayloadBound);
        this.element.addEventListener("change", this.syncPayloadBound);
        this.formElement?.addEventListener("submit", this.submitSyncBound);
        this.payloadObserver = new MutationObserver(() => {
            this.scheduleSync();
        });
        this.payloadObserver.observe(this.element, {
            subtree: true,
            childList: true,
            attributes: true,
            attributeFilter: ["value", "data-state"],
        });

        this.initialize().then(() => {
            let options = {
                refetchonexpire: true,
                spamfilter: false,
                strings: this.getStrings(),
            };

            if (this.hasChallengeUrlValue && this.challengeUrlValue) {
                options = { ...options, challengeurl: this.challengeUrlValue };
            }

            if (this.testModeValue) {
                options = { ...options, test: true, debug: true };
            }

            this.element.configure(options);
            this.scheduleSync();
        });
    }

    disconnect() {
        this.element.removeEventListener("statechange", this.syncPayloadBound);
        this.element.removeEventListener("verified", this.syncPayloadBound);
        this.element.removeEventListener("change", this.syncPayloadBound);
        this.formElement?.removeEventListener("submit", this.submitSyncBound);
        this.payloadObserver?.disconnect();
        if (this.syncTimeouts) {
            for (const timeoutId of this.syncTimeouts) {
                clearTimeout(timeoutId);
            }
        }
        this.syncTimeouts = [];
    }

    scheduleSync(flush = false) {
        if (flush) {
            this.syncPayload();
            return;
        }

        if (this.syncTimeouts) {
            for (const timeoutId of this.syncTimeouts) {
                clearTimeout(timeoutId);
            }
        }

        const delays = [0, 25, 100, 250, 500, 1000];
        this.syncTimeouts = delays.map((delay) => setTimeout(() => {
            this.syncPayload();
        }, delay));
    }

    syncPayload() {
        if (!this.hasInputIdValue) {
            return;
        }

        const formInput = document.getElementById(this.inputIdValue);
        if (!(formInput instanceof HTMLInputElement)) {
            return;
        }

        const widgetInput = this.element.querySelector('input[type="hidden"]');
        const nextValue = widgetInput instanceof HTMLInputElement ? widgetInput.value : "";
        if (!nextValue && formInput.value) {
            return;
        }

        formInput.value = nextValue;
        formInput.dispatchEvent(new Event("input", { bubbles: true }));
        formInput.dispatchEvent(new Event("change", { bubbles: true }));
    }
}
