import { cookieNames, cookieExpirations } from "@/constants/cookie-names";
import store from "@/store";
import { applicationConfig } from "@/constants/application-config";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { queryStrings } from "@/constants/query-strings";
//import { cookieParameters } from "@/constants/cookie-parameters";
/*
    Will update the cookie if present, or create a new one if not.
*/
export function updateOrCreateFunnelCookie() {
    const wasClaimRegistrationDelayed = getFunnelCookie()?.HasDelayedClaimRegistration;
    const shouldSuppressConceptFunnel = getFunnelCookie()?.SuppressConceptFunnel;

    // Set up cookie with all the props.
    setFunnelCookieProperties({
        LastTouched: new Date().toJSON(),
        SavedSessionTimeoutDate: store.getters.applicationUser.savedSessionTimeout,
        DidHeritageFunnelUpdateLast: false,
        ShouldResetState: false,
        ReferralNumber: store.getters.order.referralNumber,
        ReferralDate: store.getters.order.referralDate,
        ReferralCorrelationId: store.getters.order.referralCorrelationId,
        ReferralParentAccountNumber: store.getters.order.payment.parentAccountNumber,
        HasDelayedClaimRegistration: wasClaimRegistrationDelayed,
        SuppressConceptFunnel: shouldSuppressConceptFunnel,
        SavedSessionId: store.getters.applicationUser.savedSessionId,
    });
}

/*
    Gets the current instance of the funnel cookie.
    Returns null if cookie isn't valid JSON.
*/
export function getFunnelCookie() {
    const cookieJson = document.cookie
        ?.split("; ")
        ?.find((row) => row.startsWith(`${cookieNames.FUNNEL_SESSION_INFO}=`))
        ?.split("=")[1];

    try {
        return JSON.parse(cookieJson);
    } catch (error) {
        return null;
    }
}

/*
    Removes cookie from browser.
*/
export function deleteFunnelCookie() {
    createOrUpdateCookie(cookieNames.FUNNEL_SESSION_INFO, undefined, { maxAge: 0 });
}

/*
    Gets cookie domain value. Localhost will be empty "".
*/
export function getCookieDomainValue() {
    return isLocalhost() ? "" : `domain=${getDomainWithoutSubdomain()};`;
}

/*
    Gets value of dxdev cookie, and then extracts "did" value from it.
    Returns empty string if cookie not found or "did" string not present.
*/
export function getDeviceIdValue() {
    // Sometimes these cookie contains more than the device ID.
    const cookieValue = getCookieValueByName(cookieNames.DXDEV);
    const cookieValuesSplit = cookieValue.split("=");

    // If this is the only value, just use that.
    if (cookieValuesSplit.length === 2 && cookieValuesSplit[0] === "did") {
        return cookieValuesSplit[1];
    }

    const cookieValueMatch = cookieValue.match("^did=[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}");
    if (cookieValueMatch) {
        return cookieValueMatch[0].split("=")[1];
    }

    return "00000000-0000-0000-0000-000000000000";
}

export function regenerateDeviceId() {
    if (!isCookieSet(cookieNames.DXDEV)) {
        setCookieProperties(
            {
                [cookieNames.DXDEV]: `did=${crypto.randomUUID()}`,
            },
            { maxAge: cookieExpirations.DXDEV }
        );
    }
}

export function getUserIdValue() {
    const cookieValue = getCookieValueByName(cookieNames.FUNNEL_USER_ID);

    if (cookieValue) {
        return cookieValue;
    }

    return "00000000-0000-0000-0000-000000000000";
}

export function regenerateUserId() {
    if (!isCookieSet(cookieNames.FUNNEL_USER_ID)) {
        setCookieProperties(
            {
                [cookieNames.FUNNEL_USER_ID]: crypto.randomUUID(),
            },
            { maxAge: cookieExpirations.FUNNEL_USER_ID }
        );
    }
}

/*
    Gets value of skey cookie, returns 0 if not found.
*/
export function getSessionKeyValue() {
    const cookieValue = getCookieValueByName(cookieNames.FUNNEL_SESSION_KEY);

    if (cookieValue) {
        return cookieValue;
    }

    return 0;
}

export function setSessionKeyIfUnset(value) {
    if (!isCookieSet(cookieNames.FUNNEL_SESSION_KEY)) {
        setCookieProperties(
            {
                [cookieNames.FUNNEL_SESSION_KEY]: value,
            },
            { maxAge: cookieExpirations.FUNNEL_SESSION_KEY }
        );
    }
}

/*
    Gets value of skey cookie, returns 0 if not found.
*/
export function getSessionIdValue() {
    const cookieValue = getCookieValueByName(cookieNames.SESSION_ID);

    if (cookieValue) {
        return cookieValue;
    }

    return "00000000-0000-0000-0000-000000000000";
}

export function setSessionIdIfUnset(value) {
    if (!isCookieSet(cookieNames.SESSION_ID)) {
        setCookieProperties(
            {
                [cookieNames.SESSION_ID]: value,
            },
            { maxAge: cookieExpirations.SESSION_ID }
        );
    }
}

/* 
    Updates session ID cookie with new expiration date
*/
export function updateSessionIdCookie() {
    createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), {
        maxAge: cookieExpirations.SESSION_ID,
    });
}

export function setCookieProperties(
    properties,
    { useDefaultFunnelCookieAttributes = true, maxAge, isSecure }
) {
    if (typeof properties == "object") {
        Object.keys(properties).forEach((key) => {
            createOrUpdateCookie(key, properties[key], {
                useDefaultFunnelCookieAttributes,
                maxAge,
                isSecure,
            });
        });
    }
}

export function isCookieSet(name) {
    const val = getCookieValueByName(name);

    return !!val;
}

export function areAllSessionCookiesSet() {
    return (
        isCookieSet(cookieNames.SESSION_ID) &&
        isCookieSet(cookieNames.DXDEV) &&
        isCookieSet(cookieNames.FUNNEL_SESSION_KEY) &&
        isCookieSet(cookieNames.FUNNEL_USER_ID)
    );
}

export function refreshSessionExpiration() {
    refreshCookieExpiration(cookieNames.SESSION_ID, cookieExpirations.SESSION_ID);
    // Change 2/4/25: Don't update DXDEV to avoid Safari constraints on client-set cookies.
    // refreshCookieExpiration(cookieNames.DXDEV, cookieExpirations.DXDEV);
    refreshCookieExpiration(cookieNames.FUNNEL_USER_ID, cookieExpirations.FUNNEL_USER_ID);
    refreshCookieExpiration(cookieNames.FUNNEL_SESSION_KEY, cookieExpirations.FUNNEL_SESSION_KEY);
}

export function refreshCookieExpiration(name, expirationTime) {
    if (isCookieSet(name)) {
        createOrUpdateCookie(name, getCookieValueByName(name), { maxAge: expirationTime });
    }
}

export function getAffiliateCookies() {
    const affiliateCookies = [];
    const cookieContainingName = applicationConfig.AFFILIATE_COOKIE_CONTAINING_NAME;
    const affiliateCookiesAll = getCookiesContainingName(cookieContainingName);
    affiliateCookiesAll?.forEach((cookieName) => {
        const cookieValue = getCookieValueByName(cookieName);
        affiliateCookies.push({ CookieName: cookieName, CookieValue: cookieValue });
    });
    return affiliateCookies;
}

export function setupAdvertiserTracking() {
    var utmSource = getQuerystringParameter(queryStrings.UTM_SOURCE);
    var utmMedium = getQuerystringParameter(queryStrings.UTM_MEDIUM);
    var utmCampaign = getQuerystringParameter(queryStrings.UTM_CAMPAIGN);
    const cjEvent = getQuerystringParameter(queryStrings.CJ_EVENT);
    // helper check for if an object is defined (but maybe falsey)
    const isDefined = (x) => x !== null && x !== undefined;
    if (isDefined(cjEvent) != "") {
        const cjeCookieName = cookieNames.CJE;
        if (!isCookieSet(cjeCookieName)) {
            createCJEventCookie(cjEvent, cjeCookieName);
        }
    }
    //Firstly, Can't continue without utm-source or utm-medium.
    //Secondly, Ignore the advertiser for CJ-affiliate-click and Email-click.
    if (
        !isDefined(utmSource) ||
        isDefined(utmSource) == "" ||
        !isDefined(utmMedium) ||
        isDefined(utmMedium) == "" ||
        utmSource?.toLowerCase() == queryStrings.CJUNCTION ||
        utmMedium?.toLowerCase() == queryStrings.EMAIL ||
        utmMedium?.toLowerCase() == queryStrings.ORGANIC ||
        utmMedium?.toLowerCase() == queryStrings.ORGANIC_SOCIAL
    ) {
        return;
    }
    //UTC date time format yyyy-MM-dd/HH:mm:ss
    const currentDateTime = new Date().toISOString().replace(/T/, "/").replace(/\..+/, "");

    //Format cookie, by removing spaces and special characters.
    utmSource = FormatCookieElementName(utmSource);
    utmMedium = FormatCookieElementName(utmMedium);
    utmCampaign = FormatCookieElementName(utmCampaign);

    var cookieNameBuilder = utmSource + "_" + utmMedium;
    //Since utmCampaign is optional, ignore the rest.
    if (isDefined(utmCampaign) && utmCampaign != "") {
        cookieNameBuilder = cookieNameBuilder + "_" + utmCampaign;
    }
    const cookieName =
        (cookieNameBuilder.length > applicationConfig.COOKIE_NAME_LENGTH_MAX_LIMIT
            ? cookieNameBuilder.substring(0, applicationConfig.COOKIE_NAME_LENGTH_MAX_LIMIT)
            : cookieNameBuilder) + applicationConfig.AFFILIATE_COOKIE_CONTAINING_NAME;

    if (!isCookieSet(cookieName)) {
        const values = [
            `utm_source= ${utmSource}`,
            `utm_medium= ${utmMedium}`,
            `utm_campaign= ${utmCampaign}`,
            `timestamp= ${currentDateTime}`,
            `tagEvent= ${cjEvent}`,
        ];
        let valueString = values.join("&");
        createOrUpdateCookie(cookieName, valueString, {
            useDefaultFunnelCookieAttributes: true,
            maxAge: cookieExpirations.NON_CJ,
        });
    } else {
        const cookieValue = getCookieValueByName(cookieName);
        if (cookieValue) {
            let cookieValueObj = convertStringToObject(cookieValue);
            cookieValueObj.timestamp = currentDateTime; // Update the property
            // Set the updated cookie
            const cookieValueString = objectToString(cookieValueObj);
            createOrUpdateCookie(cookieName, cookieValueString, {
                useDefaultFunnelCookieAttributes: true,
                maxAge: cookieExpirations.NON_CJ,
            });
        }
    }
}

/*
===========================
=   PRIVATE FUNCTIONS     =
===========================
*/

/*
    Used to set properties on the funnel cookie.
    Takes an object with properties to set. Will overwrite existing properties.
*/
function setFunnelCookieProperties(properties) {
    if (typeof properties == "object") {
        let cookie = getFunnelCookie();

        if (cookie !== null) {
            Object.keys(properties).forEach((key) => {
                cookie[key] = properties[key];
            });
        }

        const cookieValueJson = JSON.stringify(cookie ?? {});
        createOrUpdateCookie(cookieNames.FUNNEL_SESSION_INFO, cookieValueJson, {});
    }
}

/*
    Used to create a cookie. 
    `useDefaultFunnelCookieAttributes` will set the path and domain to our defaults
*/
function createOrUpdateCookie(
    key,
    value = "",
    { useDefaultFunnelCookieAttributes = true, maxAge, isSecure = true, crossSiteAccess = false }
) {
    let cookieToAdd = `${key}=${value}; `;

    if (useDefaultFunnelCookieAttributes) {
        cookieToAdd += `path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()} `;
    }
    if (isSecure && !isLocalhost()) {
        cookieToAdd += `secure; `;
    }
    if (!isNaN(maxAge)) {
        cookieToAdd += `max-age=${maxAge};`;
    }
    if (crossSiteAccess && isSecure) {
        cookieToAdd += `sameSite=None;`;
    }

    document.cookie = cookieToAdd;
}

/*
    Gets current domain without the subdomain for cookie.
*/
function getDomainWithoutSubdomain() {
    let url = location.hostname;
    if (isLocalhost()) {
        return "localhost";
    }

    const urlParts = url.split(".");

    return `.${urlParts
        .slice(0)
        .slice(-(urlParts.length === 4 ? 3 : 2))
        .join(".")}`;
}

/*
    Gets cookie value by name, returns empty string if not found.
*/
function getCookieValueByName(name) {
    const value = "; " + document.cookie;
    const parts = value.split("; " + name + "=");

    if (parts.length === 2) {
        return parts.pop().split(";").shift();
    }
    return "";
}

function isLocalhost() {
    return location.hostname.includes("localhost");
}

function getCookiesContainingName(name) {
    const allCookies = document.cookie.split(";");
    const matchingCookies = [];
    for (const cookie of allCookies) {
        const [cookieName, cookieValue] = cookie.trim().split("=");
        if (cookieName.includes(name)) {
            matchingCookies.push(cookieName);
        }
    }
    return matchingCookies;
}
function createCJEventCookie(cjEvent, cookieName) {
    createOrUpdateCookie(cookieName, cjEvent, {
        useDefaultFunnelCookieAttributes: true,
        maxAge: cookieExpirations.CJE,
        isSecure: true,
        crossSiteAccess: true,
    });
}

function FormatCookieElementName(cookie) {
    // Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
    const problemSymbols = '\\s\\(\\)<>@,;:\\\\/\\[\\]\\?=\\{}"';
    const otherSymbols = "!#$%^&+\\-*|~`";
    const symbolsToIgnore = new RegExp("[" + problemSymbols + otherSymbols + "]+", "g");

    return (cookie || "").replace(symbolsToIgnore, ".");
}

function convertStringToObject(str) {
    let obj = {};
    let pairs = str.split("&");

    pairs.forEach((pair) => {
        let [key, value] = pair.split("=");
        obj[key] = value;
    });

    return obj;
}
function objectToString(obj) {
    let str = "";
    for (let key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            str += key + "=" + obj[key] + "&";
        }
    }
    // Remove the trailing '&'
    return str.slice(0, -1);
}
