// src/services/tokenStorage.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { getToken, setToken, removeToken } from './tokenStorage';

// --- Mock localStorage ---
// We create a simple in-memory storage object to simulate localStorage.
let storage: { [key: string]: string } = {};

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

// Before each test, we replace the global `localStorage` with our mock.
beforeEach(() => {
  Object.defineProperty(window, 'localStorage', {
    value: localStorageMock,
    configurable: true,
  });
  // Also clear the in-memory storage and mock call history.
  storage = {};
  vi.clearAllMocks();
});

afterEach(() => {
  // Restore any spied-on objects
  vi.restoreAllMocks();
});

// --- Test Suite ---

describe('tokenStorage', () => {
  const TOKEN_KEY = 'authToken';
  const TEST_TOKEN = 'test-jwt-token';

  describe('setToken', () => {
    it('should call localStorage.setItem with the correct key and token', () => {
      setToken(TEST_TOKEN);
      expect(localStorageMock.setItem).toHaveBeenCalledWith(TOKEN_KEY, TEST_TOKEN);
      expect(storage[TOKEN_KEY]).toBe(TEST_TOKEN);
    });

    it('should handle errors when localStorage is not available', () => {
      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
      const error = new Error('localStorage is disabled');
      localStorageMock.setItem.mockImplementationOnce(() => {
        throw error;
      });

      setToken(TEST_TOKEN);

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'SecurityError: Failed to access localStorage to set token.',
        error,
      );
    });
  });

  describe('getToken', () => {
    it('should call localStorage.getItem with the correct key', () => {
      getToken();
      expect(localStorageMock.getItem).toHaveBeenCalledWith(TOKEN_KEY);
    });

    it('should return the token if it exists', () => {
      storage[TOKEN_KEY] = TEST_TOKEN;
      const token = getToken();
      expect(token).toBe(TEST_TOKEN);
    });

    it('should return null if the token does not exist', () => {
      const token = getToken();
      expect(token).toBeNull();
    });

    it('should handle errors when localStorage is not available', () => {
      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
      const error = new Error('localStorage is disabled');
      localStorageMock.getItem.mockImplementationOnce(() => {
        throw error;
      });

      const token = getToken();

      expect(token).toBeNull();
      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'SecurityError: Failed to access localStorage to get token.',
        error,
      );
    });
  });

  describe('removeToken', () => {
    it('should call localStorage.removeItem with the correct key', () => {
      storage[TOKEN_KEY] = TEST_TOKEN; // Set a token first
      removeToken();
      expect(localStorageMock.removeItem).toHaveBeenCalledWith(TOKEN_KEY);
      expect(storage[TOKEN_KEY]).toBeUndefined();
    });

    it('should handle errors when localStorage is not available', () => {
      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
      const error = new Error('localStorage is disabled');
      localStorageMock.removeItem.mockImplementationOnce(() => {
        throw error;
      });

      removeToken();

      expect(consoleErrorSpy).toHaveBeenCalledWith(
        'SecurityError: Failed to access localStorage to remove token.',
        error,
      );
    });
  });
});