import Cookie from "js-cookie";

import { supportsCookies } from "../utils/supportsCookies.js";

import type { CookieConsentManagerOptions } from "../apply/manager.js";
import type { ClickableButtonsNamed } from "../types/types.js";

type DecisionConsentGroups = Record<number, number[]>;

type Decision = {
    /**
     * The latest given consent.
     */
    uuid: string;
    /**
     * Since v3 all UUIDs are hold in the cookie comma-separated so the history of the given consents is no longer trackable in database and statistics.
     * That means: Each consent gets it's own UUID.
     */
    previousUuids: string[];
    /**
     * When got the consent / cookie be created? Due to backwards-compatibility this can be `undefined`.
     */
    created?: Date;
    /**
     * The revision is always associated to the latest given consent.
     */
    revision: string;
    /**
     * The consent groups are always associated to the latest given consent and hold only the IDs.
     */
    consent: DecisionConsentGroups;
    /**
     * The button which got clicked. This could be useful to reproduce implicit consents.
     */
    buttonClicked: ClickableButtonsNamed;
};

/**
 * Regexp to validate and parse the cookie value with named capture groups.
 *
 * @see https://regex101.com/r/6UXL8j/2
 */
const COOKIE_VALUE_REGEXP =
    /^(?<createdAt>\d+)?:?(?<uuids>(?:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}[,]?)+):(?<revisionHash>[a-f0-9]{32}):(?<json>.*)$/;

const cookieValueValidateCache: Record<string, Decision> = {};

/**
 * Get current user decision from the cookie value in a minimal form.
 */
function getUserDecision(cookieName: CookieConsentManagerOptions["decisionCookieName"]): false | Decision {
    // Got it saved locally?
    const localConsent = localStorage.getItem(cookieName);
    if (localConsent) {
        return JSON.parse(localConsent) as Decision;
    }

    const cookieValue = Cookie.get(cookieName);
    if (!cookieValue) {
        const [supportsCookiesPrefix] = cookieName.split("-");
        if (!supportsCookies(supportsCookiesPrefix ? `${supportsCookiesPrefix}-test` : undefined)) {
            // Use our `window` object instead of cookie value
            return false;
        }

        return false;
    }

    // Save regexp- and validation time
    if (cookieValueValidateCache[cookieValue]) {
        return cookieValueValidateCache[cookieValue];
    }

    const match = cookieValue.match(COOKIE_VALUE_REGEXP);
    if (!match) {
        return false;
    }

    const { groups } = match;
    const previousUuids = groups.uuids.split(",");
    const uuid = previousUuids.shift();

    let jsonValue = JSON.parse(groups.json) as {
        d: DecisionConsentGroups;
        bc: ClickableButtonsNamed;
    };

    // Since the introduction of saving the clicked button in the cookie value, the `json` contains beside
    // the decision also the clicked button.
    if (!Object.hasOwn(jsonValue, "bc")) {
        jsonValue = {
            d: jsonValue,
            // We never will know at this time, which button the user clicked a long time ago (only in database, but in terms of performance we do not want to execute a query here)
            bc: "none",
        };
    }

    const result: Decision = {
        uuid,
        previousUuids,
        created: groups.createdAt ? new Date(+groups.createdAt * 1000) : undefined,
        revision: groups.revisionHash,
        consent: jsonValue.d,
        buttonClicked: jsonValue.bc,
    };

    cookieValueValidateCache[cookieValue] = result;

    return result;
}

export { type DecisionConsentGroups, type Decision, getUserDecision };
