import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { setCookie, getCookie, deleteCookie } from '../cookies';
import * as domain from '../domain';

// Mock the domain module
vi.mock('../domain', () => ({
  getCookieDomain: vi.fn(),
}));

describe('cookies', () => {
  beforeEach(() => {
    // Clear all cookies before each test
    document.cookie.split(';').forEach((c) => {
      document.cookie = c
        .replace(/^ +/, '')
        .replace(/=.*/, '=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/');
    });
    vi.clearAllMocks();
  });

  describe('setCookie', () => {
    it('sets a cookie with the correct name and value', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');

      setCookie('test_cookie', 'test_value');

      expect(document.cookie).toContain('test_cookie=test_value');
    });

    it('sets cookie without domain attribute for localhost', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');

      setCookie('test_cookie', 'test_value');

      // Verify cookie is set (domain attribute not included for localhost)
      expect(getCookie('test_cookie')).toBe('test_value');
    });

    it('includes domain attribute for non-localhost domains', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('.lvh.me');

      // In jsdom, cookies with domain attributes may not be readable
      // We verify that setCookie doesn't throw and the function is called correctly
      expect(() => setCookie('test_cookie', 'test_value')).not.toThrow();
      expect(domain.getCookieDomain).toHaveBeenCalled();
    });

    it('sets expiration based on days parameter', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');

      setCookie('test_cookie', 'test_value', 1);

      expect(getCookie('test_cookie')).toBe('test_value');
    });

    it('defaults to 7 days expiration', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');

      setCookie('test_cookie', 'test_value');

      expect(getCookie('test_cookie')).toBe('test_value');
    });
  });

  describe('getCookie', () => {
    it('returns null when cookie does not exist', () => {
      expect(getCookie('nonexistent')).toBeNull();
    });

    it('returns the correct value for an existing cookie', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');
      setCookie('my_cookie', 'my_value');

      expect(getCookie('my_cookie')).toBe('my_value');
    });

    it('handles multiple cookies correctly', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');
      setCookie('cookie1', 'value1');
      setCookie('cookie2', 'value2');
      setCookie('cookie3', 'value3');

      expect(getCookie('cookie1')).toBe('value1');
      expect(getCookie('cookie2')).toBe('value2');
      expect(getCookie('cookie3')).toBe('value3');
    });

    it('handles cookies with special characters in values', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');
      setCookie('special', 'value%20with%20spaces');

      expect(getCookie('special')).toBe('value%20with%20spaces');
    });

    it('handles whitespace in cookie string', () => {
      // Set a cookie with potential whitespace
      document.cookie = '  spaced_cookie = spaced_value';

      // The getCookie function should handle leading whitespace
      expect(getCookie('spaced_cookie')).toBeTruthy();
    });

    it('does not return partial matches', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');
      setCookie('access_token', 'token123');

      expect(getCookie('access')).toBeNull();
      expect(getCookie('token')).toBeNull();
      expect(getCookie('access_token')).toBe('token123');
    });
  });

  describe('deleteCookie', () => {
    it('removes an existing cookie', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');
      setCookie('to_delete', 'value');
      expect(getCookie('to_delete')).toBe('value');

      deleteCookie('to_delete');

      expect(getCookie('to_delete')).toBeNull();
    });

    it('does not throw when deleting non-existent cookie', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');

      expect(() => deleteCookie('nonexistent')).not.toThrow();
    });

    it('uses correct domain attribute when deleting', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('.lvh.me');
      setCookie('domain_cookie', 'value');

      deleteCookie('domain_cookie');

      expect(getCookie('domain_cookie')).toBeNull();
    });

    it('deletes only the specified cookie', () => {
      vi.mocked(domain.getCookieDomain).mockReturnValue('localhost');
      setCookie('keep1', 'value1');
      setCookie('delete_me', 'value2');
      setCookie('keep2', 'value3');

      deleteCookie('delete_me');

      expect(getCookie('keep1')).toBe('value1');
      expect(getCookie('delete_me')).toBeNull();
      expect(getCookie('keep2')).toBe('value3');
    });
  });
});
