import { describe, it, expect, vi } from "vitest";
import { ANALYTICS_EVENTS, fireAnalyticsEvent } from "../lib/analytics";

describe("analytics", () => {
  describe("ANALYTICS_EVENTS constants", () => {
    it("exports all required event names", () => {
      expect(ANALYTICS_EVENTS.BOOKING_STEP_SERVICE).toBe("booking_step_service");
      expect(ANALYTICS_EVENTS.BOOKING_STEP_TIME).toBe("booking_step_time");
      expect(ANALYTICS_EVENTS.BOOKING_STEP_CONTACT).toBe("booking_step_contact");
      expect(ANALYTICS_EVENTS.BOOKING_STEP_SUBMIT).toBe("booking_step_submit");
      expect(ANALYTICS_EVENTS.BOOKING_CONFIRMED).toBe("booking_confirmed");
      expect(ANALYTICS_EVENTS.BOOKING_ERROR).toBe("booking_error");
    });

    it("has no duplicate event names", () => {
      const values = Object.values(ANALYTICS_EVENTS);
      const unique = new Set(values);
      expect(unique.size).toBe(values.length);
    });
  });

  describe("fireAnalyticsEvent", () => {
    it("dispatches a CustomEvent with the correct event name", () => {
      const listener = vi.fn();
      window.addEventListener(ANALYTICS_EVENTS.BOOKING_STEP_SERVICE, listener);
      fireAnalyticsEvent(ANALYTICS_EVENTS.BOOKING_STEP_SERVICE, { step: "service", flow: "public" });
      expect(listener).toHaveBeenCalledTimes(1);
      const event = listener.mock.calls[0]![0] as CustomEvent;
      expect(event.type).toBe("booking_step_service");
      expect(event.detail.step).toBe("service");
      expect(event.detail.flow).toBe("public");
      expect(event.detail.timestamp).toBeDefined();
      window.removeEventListener(ANALYTICS_EVENTS.BOOKING_STEP_SERVICE, listener);
    });

    it("includes a timestamp in the event detail", () => {
      const listener = vi.fn();
      window.addEventListener(ANALYTICS_EVENTS.BOOKING_CONFIRMED, listener);
      fireAnalyticsEvent(ANALYTICS_EVENTS.BOOKING_CONFIRMED, { step: "confirmed", flow: "public" });
      const event = listener.mock.calls[0]![0] as CustomEvent;
      expect(event.detail.timestamp).toBeTruthy();
      expect(new Date(event.detail.timestamp as string)).toBeInstanceOf(Date);
      window.removeEventListener(ANALYTICS_EVENTS.BOOKING_CONFIRMED, listener);
    });

    it("does not throw when called with no payload", () => {
      expect(() => {
        fireAnalyticsEvent(ANALYTICS_EVENTS.BOOKING_ERROR, {});
      }).not.toThrow();
    });

    it("does not throw when window.dispatchEvent throws", () => {
      const original = window.dispatchEvent;
      window.dispatchEvent = () => {
        throw new Error("analytics blocked");
      };
      expect(() => {
        fireAnalyticsEvent(ANALYTICS_EVENTS.BOOKING_STEP_SUBMIT, { step: "submit", flow: "public" });
      }).not.toThrow();
      window.dispatchEvent = original;
    });

    it("fires events for all event types", () => {
      const events = Object.values(ANALYTICS_EVENTS);
      for (const eventName of events) {
        const listener = vi.fn();
        window.addEventListener(eventName, listener);
        fireAnalyticsEvent(eventName as typeof events[number], { step: "test", flow: "public" });
        expect(listener).toHaveBeenCalledTimes(1);
        window.removeEventListener(eventName, listener);
      }
    });

    it("does not include PII in payload", () => {
      // Payload only contains step, flow, and timestamp — no names, emails, or phones
      const payload = { step: "contact", flow: "public" };
      const keys = Object.keys(payload);
      const piish = ["name", "email", "phone", "clientName", "clientEmail", "clientPhone", "petName"];
      const hasPII = piish.some((k) => keys.includes(k));
      expect(hasPII).toBe(false);
    });
  });
});
