/**
 * Unit tests for the Meta Pixel utilities
 */
import {
    isValidMetaPixelId,
    initMetaPixel,
    trackMetaPixelEvent,
    hasGlobalPrivacyControlOptOut,
    __resetMetaPixelForTests,
} from "./metaPixel";

/** Set or clear the GPC browser signal for a test. */
function setGlobalPrivacyControl(value: boolean | undefined): void {
    if (value === undefined) {
        delete (navigator as Navigator & { globalPrivacyControl?: boolean }).globalPrivacyControl;
        return;
    }
    Object.defineProperty(navigator, "globalPrivacyControl", {
        value,
        configurable: true,
    });
}

describe("metaPixel", () => {
    beforeEach(() => {
        __resetMetaPixelForTests();
        setGlobalPrivacyControl(undefined);
        // Remove any injected loader scripts between tests
        document
            .querySelectorAll('script[src*="fbevents.js"]')
            .forEach((node) => node.parentNode?.removeChild(node));
    });

    afterEach(() => {
        jest.restoreAllMocks();
        setGlobalPrivacyControl(undefined);
    });

    describe("isValidMetaPixelId", () => {
        it("accepts a typical numeric pixel id", () => {
            expect(isValidMetaPixelId("4092252157584843")).toBe(true);
        });

        it("trims surrounding whitespace", () => {
            expect(isValidMetaPixelId("  4092252157584843  ")).toBe(true);
        });

        it("rejects non-numeric, empty, and non-string values", () => {
            expect(isValidMetaPixelId("not-a-pixel")).toBe(false);
            expect(isValidMetaPixelId("")).toBe(false);
            expect(isValidMetaPixelId("123")).toBe(false); // too short
            expect(isValidMetaPixelId(undefined)).toBe(false);
            expect(isValidMetaPixelId(40922521 as unknown)).toBe(false);
        });
    });

    describe("initMetaPixel", () => {
        it("installs fbq, enables LDU, and tracks PageView for a valid id", () => {
            const result = initMetaPixel("4092252157584843");

            expect(result).toBe(true);
            expect(typeof window.fbq).toBe("function");
            const calls = (window.fbq as unknown as { queue: unknown[][] }).queue;
            expect(calls).toEqual([
                ["dataProcessingOptions", ["LDU"], 0, 0],
                ["init", "4092252157584843"],
                ["track", "PageView"],
            ]);
        });

        it("enables Limited Data Use before init", () => {
            initMetaPixel("4092252157584843");

            const calls = (window.fbq as unknown as { queue: unknown[][] }).queue;
            const lduIndex = calls.findIndex((c) => c[0] === "dataProcessingOptions");
            const initIndex = calls.findIndex((c) => c[0] === "init");
            expect(lduIndex).toBeGreaterThanOrEqual(0);
            expect(lduIndex).toBeLessThan(initIndex);
        });

        it("injects the fbevents loader script once", () => {
            initMetaPixel("4092252157584843");
            initMetaPixel("4092252157584843");

            const scripts = document.querySelectorAll('script[src*="fbevents.js"]');
            expect(scripts.length).toBe(1);
        });

        it("does not re-init the same pixel id", () => {
            initMetaPixel("4092252157584843");
            initMetaPixel("4092252157584843");

            const calls = (window.fbq as unknown as { queue: unknown[][] }).queue;
            // init + PageView should only appear once
            expect(calls.filter((c) => c[0] === "init").length).toBe(1);
        });

        it("registers our pixel under a pre-existing host-page fbq", () => {
            // Host page already loaded its own pixel
            const hostFbq = jest.fn() as unknown as typeof window.fbq;
            window.fbq = hostFbq;

            const result = initMetaPixel("4092252157584843");

            expect(result).toBe(true);
            // We reuse the host fbq (Multi-Pixel model) rather than re-bootstrapping
            expect(window.fbq).toBe(hostFbq);
            expect(hostFbq).toHaveBeenCalledWith("init", "4092252157584843");
            expect(hostFbq).toHaveBeenCalledWith("track", "PageView");
            // Must NOT toggle global LDU on the host's fbq — dataProcessingOptions
            // is a global command and would override the host's own pixel settings.
            expect(hostFbq).not.toHaveBeenCalledWith("dataProcessingOptions", ["LDU"], 0, 0);
        });

        it("returns false and warns for an invalid id", () => {
            const warn = jest.spyOn(console, "warn").mockImplementation(() => undefined);

            expect(initMetaPixel("nope")).toBe(false);
            expect(window.fbq).toBeUndefined();
            expect(warn).toHaveBeenCalled();
        });

        it("returns false silently for a missing id", () => {
            const warn = jest.spyOn(console, "warn").mockImplementation(() => undefined);

            expect(initMetaPixel(undefined)).toBe(false);
            expect(window.fbq).toBeUndefined();
            expect(warn).not.toHaveBeenCalled();
        });

        it("does not load the pixel when Global Privacy Control opts out", () => {
            setGlobalPrivacyControl(true);

            const result = initMetaPixel("4092252157584843");

            expect(result).toBe(false);
            // Pixel must not be bootstrapped or loaded at all
            expect(window.fbq).toBeUndefined();
            expect(document.querySelectorAll('script[src*="fbevents.js"]').length).toBe(0);
        });
    });

    describe("hasGlobalPrivacyControlOptOut", () => {
        it("is false when GPC is unset", () => {
            expect(hasGlobalPrivacyControlOptOut()).toBe(false);
        });

        it("is true only when GPC is exactly true", () => {
            setGlobalPrivacyControl(true);
            expect(hasGlobalPrivacyControlOptOut()).toBe(true);

            setGlobalPrivacyControl(false);
            expect(hasGlobalPrivacyControlOptOut()).toBe(false);
        });
    });

    describe("trackMetaPixelEvent", () => {
        it("tracks an event after init", () => {
            initMetaPixel("4092252157584843");
            trackMetaPixelEvent("Lead");

            const calls = (window.fbq as unknown as { queue: unknown[][] }).queue;
            expect(calls).toContainEqual(["track", "Lead"]);
        });

        it("passes event parameters when provided", () => {
            initMetaPixel("4092252157584843");
            trackMetaPixelEvent("Lead", { value: 10, currency: "USD" });

            const calls = (window.fbq as unknown as { queue: unknown[][] }).queue;
            expect(calls).toContainEqual(["track", "Lead", { value: 10, currency: "USD" }]);
        });

        it("passes the eventID (4th arg) for dedup when an eventId is provided", () => {
            initMetaPixel("4092252157584843");
            trackMetaPixelEvent("Lead", undefined, "shared-evt-123");

            const calls = (window.fbq as unknown as { queue: unknown[][] }).queue;
            expect(calls).toContainEqual(["track", "Lead", {}, { eventID: "shared-evt-123" }]);
        });

        it("is a no-op when the pixel was never initialized", () => {
            expect(() => trackMetaPixelEvent("Lead")).not.toThrow();
            expect(window.fbq).toBeUndefined();
        });

        it("is a no-op when window.fbq exists (host page) but we never initialized", () => {
            // Simulate a host page that loaded its own pixel
            const hostFbq = jest.fn();
            window.fbq = hostFbq as unknown as typeof window.fbq;

            trackMetaPixelEvent("Lead");

            // We must not credit the host page's pixel with our Lead
            expect(hostFbq).not.toHaveBeenCalled();
        });

        it("is a no-op when GPC opts out after the pixel was initialized", () => {
            initMetaPixel("4092252157584843");

            // Opt-out signal appears after init (e.g. an extension sets it later)
            setGlobalPrivacyControl(true);
            trackMetaPixelEvent("Lead");

            const calls = (window.fbq as unknown as { queue: unknown[][] }).queue;
            expect(calls).not.toContainEqual(["track", "Lead"]);
        });
    });
});
