import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import {
  CATEGORIES_COOKIE_NAME,
  CONSENT_COOKIE_NAME,
  getCategoryConsent,
  getConsentStatus,
  resetAnalyticsIdentity,
  setCookieConsent,
} from './cookieConsent';

/**
 * Helper to clear a cookie by name.
 */
function clearCookie(name: string): void {
  document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}

/**
 * Helper to set a raw cookie string.
 */
function setRawCookie(name: string, value: string): void {
  document.cookie = `${name}=${encodeURIComponent(value)}; path=/;`;
}

beforeEach(() => {
  clearCookie(CONSENT_COOKIE_NAME);
  clearCookie(CATEGORIES_COOKIE_NAME);
});

afterEach(() => {
  clearCookie(CONSENT_COOKIE_NAME);
  clearCookie(CATEGORIES_COOKIE_NAME);
  delete (window as unknown as Record<string, unknown>).posthog;
});

// ============================================
// getConsentStatus
// ============================================

describe('getConsentStatus', () => {
  it('returns null when no consent cookie exists', () => {
    expect(getConsentStatus()).toBeNull();
  });

  it('returns "accepted" when cookie_consent=accepted', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'accepted');
    expect(getConsentStatus()).toBe('accepted');
  });

  it('returns "declined" when cookie_consent=declined', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'declined');
    expect(getConsentStatus()).toBe('declined');
  });

  it('returns null for an unrecognized cookie value', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'unknown_value');
    expect(getConsentStatus()).toBeNull();
  });

  it('returns null for an empty cookie value', () => {
    setRawCookie(CONSENT_COOKIE_NAME, '');
    expect(getConsentStatus()).toBeNull();
  });
});

// ============================================
// setCookieConsent
// ============================================

describe('setCookieConsent', () => {
  it('writes a readable cookie with the given name and value', () => {
    setCookieConsent(CONSENT_COOKIE_NAME, 'accepted');

    expect(getConsentStatus()).toBe('accepted');
  });

  it('URL-encodes the value', () => {
    // Special chars in value should not corrupt subsequent reads
    setCookieConsent('test_cookie', 'hello world');
    const match = document.cookie.match(/test_cookie=([^;]*)/);
    expect(match).not.toBeNull();
    expect(decodeURIComponent(match![1])).toBe('hello world');
    clearCookie('test_cookie');
  });

  it('sets an expiry date in the future', () => {
    // The cookie must not have already expired; a readable cookie implies a future expiry.
    setCookieConsent(CONSENT_COOKIE_NAME, 'accepted');
    // If cookie is readable, the expiry is valid
    expect(document.cookie).toContain(CONSENT_COOKIE_NAME);
  });
});

// ============================================
// getCategoryConsent
// ============================================

describe('getCategoryConsent', () => {
  it('always returns true for "necessary" regardless of consent status', () => {
    // No consent cookie at all
    expect(getCategoryConsent('necessary')).toBe(true);

    setRawCookie(CONSENT_COOKIE_NAME, 'declined');
    expect(getCategoryConsent('necessary')).toBe(true);
  });

  it('returns false for analytics when no consent cookie exists', () => {
    expect(getCategoryConsent('analytics')).toBe(false);
  });

  it('returns false for marketing when no consent cookie exists', () => {
    expect(getCategoryConsent('marketing')).toBe(false);
  });

  it('returns true for analytics after accept-all (no categories cookie)', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'accepted');
    // No categories cookie → fall back to status === 'accepted'
    expect(getCategoryConsent('analytics')).toBe(true);
  });

  it('returns true for marketing after accept-all (no categories cookie)', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'accepted');
    expect(getCategoryConsent('marketing')).toBe(true);
  });

  it('returns false for analytics after decline-all (no categories cookie)', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'declined');
    expect(getCategoryConsent('analytics')).toBe(false);
  });

  it('reads per-category consent from the categories cookie when present', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'accepted');
    const categories = { necessary: true, analytics: true, marketing: false };
    setRawCookie(CATEGORIES_COOKIE_NAME, JSON.stringify(categories));

    expect(getCategoryConsent('analytics')).toBe(true);
    expect(getCategoryConsent('marketing')).toBe(false);
  });

  it('returns false when analytics is explicitly false in categories cookie', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'accepted');
    const categories = { necessary: true, analytics: false, marketing: false };
    setRawCookie(CATEGORIES_COOKIE_NAME, JSON.stringify(categories));

    expect(getCategoryConsent('analytics')).toBe(false);
  });

  it('falls back to status === "accepted" when categories cookie is malformed JSON', () => {
    setRawCookie(CONSENT_COOKIE_NAME, 'accepted');
    // Directly write a malformed cookie (bypass setCookieConsent encoding)
    document.cookie = `${CATEGORIES_COOKIE_NAME}=not-valid-json; path=/;`;

    // Malformed → fall back to status 'accepted' → true
    expect(getCategoryConsent('analytics')).toBe(true);
  });
});

// ============================================
// resetAnalyticsIdentity
// ============================================

describe('resetAnalyticsIdentity', () => {
  it('calls posthog.reset() when PostHog is available', () => {
    const resetSpy = vi.fn();
    (window as unknown as Record<string, unknown>).posthog = { reset: resetSpy };

    resetAnalyticsIdentity();

    expect(resetSpy).toHaveBeenCalledOnce();
  });

  it('does not throw when PostHog is unavailable', () => {
    delete (window as unknown as Record<string, unknown>).posthog;

    expect(() => resetAnalyticsIdentity()).not.toThrow();
  });

  it('does not throw when posthog exists but has no reset method', () => {
    (window as unknown as Record<string, unknown>).posthog = { capture: vi.fn() };

    expect(() => resetAnalyticsIdentity()).not.toThrow();
  });
});
