import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TokenStorage, _resetTokenMemory } from './tokenStorage';

// Mock localStorage
const localStorageMock = (() => {
  let store: Record<string, string> = {};

  return {
    getItem: vi.fn((key: string) => store[key] || null),
    setItem: vi.fn((key: string, value: string) => {
      store[key] = value.toString();
    }),
    removeItem: vi.fn((key: string) => {
      delete store[key];
    }),
    clear: vi.fn(() => {
      store = {};
    }),
  };
})();

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock,
  writable: true,
});

describe('TokenStorage', () => {
  beforeEach(() => {
    localStorageMock.clear();
    vi.clearAllMocks();
    // SECURITY: Réinitialiser la mémoire pour chaque test
    _resetTokenMemory();
  });

  describe('setTokens', () => {
    it('should store access token and refresh token in localStorage', () => {
      const accessToken = 'test-access-token';
      const refreshToken = 'test-refresh-token';

      TokenStorage.setTokens(accessToken, refreshToken);

      expect(localStorageMock.setItem).toHaveBeenCalledWith(
        'veza_access_token',
        accessToken,
      );
      expect(localStorageMock.setItem).toHaveBeenCalledWith(
        'veza_refresh_token',
        refreshToken,
      );
      expect(localStorageMock.setItem).toHaveBeenCalledTimes(2);
    });

    it('should overwrite existing tokens when called multiple times', () => {
      TokenStorage.setTokens('token1', 'refresh1');
      TokenStorage.setTokens('token2', 'refresh2');

      expect(localStorageMock.setItem).toHaveBeenCalledTimes(4);
      // Check that the last two calls set the new tokens
      const calls = localStorageMock.setItem.mock.calls;
      expect(calls[2]).toEqual(['veza_access_token', 'token2']);
      expect(calls[3]).toEqual(['veza_refresh_token', 'refresh2']);
    });
  });

  describe('getAccessToken', () => {
    it('should return access token from localStorage', () => {
      const accessToken = 'test-access-token';
      localStorageMock.setItem('veza_access_token', accessToken);

      const result = TokenStorage.getAccessToken();

      expect(result).toBe(accessToken);
      expect(localStorageMock.getItem).toHaveBeenCalledWith(
        'veza_access_token',
      );
    });

    it('should return null if access token does not exist', () => {
      const result = TokenStorage.getAccessToken();

      expect(result).toBeNull();
      expect(localStorageMock.getItem).toHaveBeenCalledWith(
        'veza_access_token',
      );
    });
  });

  describe('getRefreshToken', () => {
    it('should return refresh token from localStorage', () => {
      const refreshToken = 'test-refresh-token';
      localStorageMock.setItem('veza_refresh_token', refreshToken);

      const result = TokenStorage.getRefreshToken();

      expect(result).toBe(refreshToken);
      expect(localStorageMock.getItem).toHaveBeenCalledWith(
        'veza_refresh_token',
      );
    });

    it('should return null if refresh token does not exist', () => {
      const result = TokenStorage.getRefreshToken();

      expect(result).toBeNull();
      expect(localStorageMock.getItem).toHaveBeenCalledWith(
        'veza_refresh_token',
      );
    });
  });

  describe('clearTokens', () => {
    it('should remove both tokens from localStorage', () => {
      TokenStorage.setTokens('access-token', 'refresh-token');
      localStorageMock.setItem.mockClear();

      TokenStorage.clearTokens();

      expect(localStorageMock.removeItem).toHaveBeenCalledWith(
        'veza_access_token',
      );
      expect(localStorageMock.removeItem).toHaveBeenCalledWith(
        'veza_refresh_token',
      );
      expect(localStorageMock.removeItem).toHaveBeenCalledTimes(2);
    });

    it('should not throw error if tokens do not exist', () => {
      expect(() => TokenStorage.clearTokens()).not.toThrow();
      expect(localStorageMock.removeItem).toHaveBeenCalledTimes(2);
    });
  });

  describe('hasTokens', () => {
    it('should return true when both tokens exist', () => {
      TokenStorage.setTokens('access-token', 'refresh-token');

      const result = TokenStorage.hasTokens();

      expect(result).toBe(true);
    });

    it('should return false when access token is missing', () => {
      localStorageMock.setItem('veza_refresh_token', 'refresh-token');

      const result = TokenStorage.hasTokens();

      expect(result).toBe(false);
    });

    it('should return false when refresh token is missing', () => {
      localStorageMock.setItem('veza_access_token', 'access-token');

      const result = TokenStorage.hasTokens();

      expect(result).toBe(false);
    });

    it('should return false when both tokens are missing', () => {
      const result = TokenStorage.hasTokens();

      expect(result).toBe(false);
    });

    it('should return false when tokens are empty strings', () => {
      localStorageMock.setItem('veza_access_token', '');
      localStorageMock.setItem('veza_refresh_token', '');

      const result = TokenStorage.hasTokens();

      expect(result).toBe(false);
    });
  });

  describe('integration', () => {
    it('should work correctly with full token lifecycle', () => {
      // Initially no tokens
      expect(TokenStorage.hasTokens()).toBe(false);
      expect(TokenStorage.getAccessToken()).toBeNull();
      expect(TokenStorage.getRefreshToken()).toBeNull();

      // Store tokens
      TokenStorage.setTokens('access-123', 'refresh-456');
      expect(TokenStorage.hasTokens()).toBe(true);
      expect(TokenStorage.getAccessToken()).toBe('access-123');
      expect(TokenStorage.getRefreshToken()).toBe('refresh-456');

      // Clear tokens
      TokenStorage.clearTokens();
      expect(TokenStorage.hasTokens()).toBe(false);
      expect(TokenStorage.getAccessToken()).toBeNull();
      expect(TokenStorage.getRefreshToken()).toBeNull();
    });
  });
});
