import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ConsentStorage } from './ConsentStorage';
import type { ConsentConfig, ConsentState } from '../types';

describe('ConsentStorage', () => {
  let storage: ConsentStorage;
  const mockConfig: ConsentConfig = {
    apiEndpoint: 'https://api.example.com',
    siteId: 'test-site',
    debug: false,
    consent: {
      rememberDays: 365,
    },
  };

  const mockConsent: ConsentState = {
    categories: {
      essential: true,
      functional: true,
      analytics: false,
      marketing: false,
      social: false,
    },
    vendors: {},
    timestamp: '2024-01-15T10:00:00.000Z',
    version: '1.0.0',
  };

  beforeEach(() => {
    localStorage.clear();
    storage = new ConsentStorage(mockConfig);
  });

  describe('constructor', () => {
    it('should create storage with site-specific key', () => {
      expect(storage).toBeDefined();
    });
  });

  describe('get', () => {
    it('should return null when no consent stored', () => {
      const result = storage.get();
      expect(result).toBeNull();
    });

    it('should return consent when valid data exists', () => {
      storage.set(mockConsent);
      const result = storage.get();

      expect(result).toBeDefined();
      expect(result?.categories.essential).toBe(true);
      expect(result?.categories.analytics).toBe(false);
    });

    it('should return null and clear when version mismatch', () => {
      // Manually set invalid version in storage
      const storageKey = `bp_consent_${mockConfig.siteId}`;
      localStorage.setItem(
        storageKey,
        JSON.stringify({
          version: 'invalid',
          consent: mockConsent,
          signature: 'test',
        })
      );

      const result = storage.get();
      expect(result).toBeNull();
    });

    it('should return null and clear when signature invalid', () => {
      const storageKey = `bp_consent_${mockConfig.siteId}`;
      localStorage.setItem(
        storageKey,
        JSON.stringify({
          version: '1',
          consent: mockConsent,
          signature: 'invalid-signature',
        })
      );

      const result = storage.get();
      expect(result).toBeNull();
    });

    it('should return null when JSON is invalid', () => {
      const storageKey = `bp_consent_${mockConfig.siteId}`;
      localStorage.setItem(storageKey, 'invalid-json');

      const result = storage.get();
      expect(result).toBeNull();
    });
  });

  describe('set', () => {
    it('should store consent in localStorage', () => {
      storage.set(mockConsent);

      const storageKey = `bp_consent_${mockConfig.siteId}`;
      const stored = localStorage.getItem(storageKey);

      expect(stored).toBeDefined();
      expect(stored).toContain('"version":"1"');
    });

    it('should set a cookie for SSR support', () => {
      storage.set(mockConsent);

      expect(document.cookie).toContain(`bp_consent_${mockConfig.siteId}`);
    });

    it('should include signature in stored data', () => {
      storage.set(mockConsent);

      const storageKey = `bp_consent_${mockConfig.siteId}`;
      const stored = JSON.parse(localStorage.getItem(storageKey) || '{}');

      expect(stored.signature).toBeDefined();
      expect(typeof stored.signature).toBe('string');
    });
  });

  describe('clear', () => {
    it('should remove consent from localStorage', () => {
      storage.set(mockConsent);
      storage.clear();

      const result = storage.get();
      expect(result).toBeNull();
    });

    it('should clear the cookie', () => {
      storage.set(mockConsent);
      storage.clear();

      // Cookie should be cleared (expired)
      expect(document.cookie).toContain('expires=');
    });
  });

  describe('exists', () => {
    it('should return false when no consent exists', () => {
      expect(storage.exists()).toBe(false);
    });

    it('should return true when consent exists', () => {
      storage.set(mockConsent);
      expect(storage.exists()).toBe(true);
    });
  });

  describe('signature verification', () => {
    it('should detect tampered consent data', () => {
      storage.set(mockConsent);

      const storageKey = `bp_consent_${mockConfig.siteId}`;
      const stored = JSON.parse(localStorage.getItem(storageKey) || '{}');

      // Tamper with the data
      stored.consent.categories.marketing = true;
      localStorage.setItem(storageKey, JSON.stringify(stored));

      const result = storage.get();
      expect(result).toBeNull(); // Signature mismatch should clear
    });
  });

  describe('debug mode', () => {
    it('should log when debug is enabled', () => {
      const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});

      const debugStorage = new ConsentStorage({
        ...mockConfig,
        debug: true,
      });

      debugStorage.set(mockConsent);

      expect(consoleSpy).toHaveBeenCalled();
      consoleSpy.mockRestore();
    });
  });

  describe('cookie settings', () => {
    it('should set Secure flag on HTTPS', () => {
      storage.set(mockConsent);
      expect(document.cookie).toContain('Secure');
    });

    it('should set SameSite=Lax', () => {
      storage.set(mockConsent);
      expect(document.cookie).toContain('SameSite=Lax');
    });

    it('should set expiration based on rememberDays', () => {
      storage.set(mockConsent);
      expect(document.cookie).toContain('expires=');
    });
  });

  describe('different sites', () => {
    it('should isolate storage by siteId', () => {
      const storage1 = new ConsentStorage({ ...mockConfig, siteId: 'site-1' });
      const storage2 = new ConsentStorage({ ...mockConfig, siteId: 'site-2' });

      storage1.set(mockConsent);

      expect(storage1.exists()).toBe(true);
      expect(storage2.exists()).toBe(false);
    });
  });
});
