import axios from 'axios';
import api, { authService, colorService, filamentService } from '../src/services/api';

// Get the mock axios instance that was created
const mockAxiosInstance = (axios.create as jest.Mock).mock.results[0].value;

// Mock localStorage
const localStorageMock = {
  getItem: jest.fn(),
  setItem: jest.fn(),
  removeItem: jest.fn(),
  clear: jest.fn(),
};
(global as any).localStorage = localStorageMock;

// Mock window.location
const mockLocation = {
  pathname: '/',
  href: '',
};

// Only define location if it doesn't exist or is configurable
if (!Object.getOwnPropertyDescriptor(window, 'location') || 
    Object.getOwnPropertyDescriptor(window, 'location')?.configurable) {
  Object.defineProperty(window, 'location', {
    value: mockLocation,
    configurable: true,
    writable: true
  });
} else {
  // If location exists and is not configurable, we'll work with the existing object
  Object.assign(window.location, mockLocation);
}

describe('API Service Tests', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    
    // Clear localStorage mocks
    localStorageMock.getItem.mockClear();
    localStorageMock.removeItem.mockClear();
    localStorageMock.setItem.mockClear();
    
    // Reset window location
    mockLocation.pathname = '/';
    mockLocation.href = '';
  });

  describe('Auth Service', () => {
    it('should login successfully', async () => {
      const mockResponse = { data: { token: 'test-token', user: 'admin' } };
      mockAxiosInstance.post.mockResolvedValue(mockResponse);

      const result = await authService.login('admin', 'password123');

      expect(mockAxiosInstance.post).toHaveBeenCalledWith('/login', {
        username: 'admin',
        password: 'password123'
      });
      expect(result).toEqual(mockResponse.data);
    });

    it('should handle login failure', async () => {
      const error = new Error('Invalid credentials');
      mockAxiosInstance.post.mockRejectedValue(error);

      await expect(authService.login('admin', 'wrong')).rejects.toThrow('Invalid credentials');
    });
  });

  describe('Color Service', () => {
    it('should get all colors', async () => {
      const mockColors = [
        { id: '1', name: 'Red', hex: '#FF0000' },
        { id: '2', name: 'Blue', hex: '#0000FF' }
      ];
      mockAxiosInstance.get.mockResolvedValue({ data: mockColors });

      const result = await colorService.getAll();

      expect(mockAxiosInstance.get).toHaveBeenCalledWith('/colors');
      expect(result).toEqual(mockColors);
    });

    it('should create a color', async () => {
      const newColor = { name: 'Green', hex: '#00FF00', cena_refill: 100, cena_spulna: 150 };
      const mockResponse = { id: '3', ...newColor };
      mockAxiosInstance.post.mockResolvedValue({ data: mockResponse });

      const result = await colorService.create(newColor);

      expect(mockAxiosInstance.post).toHaveBeenCalledWith('/colors', newColor);
      expect(result).toEqual(mockResponse);
    });

    it('should update a color', async () => {
      const colorId = '1';
      const updateData = { name: 'Dark Red', hex: '#8B0000' };
      const mockResponse = { id: colorId, ...updateData };
      mockAxiosInstance.put.mockResolvedValue({ data: mockResponse });

      const result = await colorService.update(colorId, updateData);

      expect(mockAxiosInstance.put).toHaveBeenCalledWith(`/colors/${colorId}`, updateData);
      expect(result).toEqual(mockResponse);
    });

    it('should delete a color', async () => {
      const colorId = '1';
      const mockResponse = { success: true };
      mockAxiosInstance.delete.mockResolvedValue({ data: mockResponse });

      const result = await colorService.delete(colorId);

      expect(mockAxiosInstance.delete).toHaveBeenCalledWith(`/colors/${colorId}`);
      expect(result).toEqual(mockResponse);
    });
  });

  describe('Filament Service', () => {
    it('should get all filaments with cache buster', async () => {
      const mockFilaments = [
        { id: '1', tip: 'PLA', boja: 'Red' },
        { id: '2', tip: 'PETG', boja: 'Blue' }
      ];
      mockAxiosInstance.get.mockResolvedValue({ data: mockFilaments });

      // Mock Date.now()
      const originalDateNow = Date.now;
      const mockTimestamp = 1234567890;
      Date.now = jest.fn(() => mockTimestamp);

      const result = await filamentService.getAll();

      expect(mockAxiosInstance.get).toHaveBeenCalledWith(`/filaments?_t=${mockTimestamp}`);
      expect(result).toEqual(mockFilaments);

      // Restore Date.now
      Date.now = originalDateNow;
    });

    it('should create a filament', async () => {
      const newFilament = {
        tip: 'ABS',
        finish: 'Matte',
        boja: 'Black',
        boja_hex: '#000000'
      };
      const mockResponse = { id: '3', ...newFilament };
      mockAxiosInstance.post.mockResolvedValue({ data: mockResponse });

      const result = await filamentService.create(newFilament);

      expect(mockAxiosInstance.post).toHaveBeenCalledWith('/filaments', newFilament);
      expect(result).toEqual(mockResponse);
    });

    it('should update a filament', async () => {
      const filamentId = '1';
      const updateData = {
        tip: 'PLA+',
        finish: 'Silk',
        cena: '4500'
      };
      const mockResponse = { id: filamentId, ...updateData };
      mockAxiosInstance.put.mockResolvedValue({ data: mockResponse });

      const result = await filamentService.update(filamentId, updateData);

      expect(mockAxiosInstance.put).toHaveBeenCalledWith(`/filaments/${filamentId}`, updateData);
      expect(result).toEqual(mockResponse);
    });

    it('should delete a filament', async () => {
      const filamentId = '1';
      const mockResponse = { success: true };
      mockAxiosInstance.delete.mockResolvedValue({ data: mockResponse });

      const result = await filamentService.delete(filamentId);

      expect(mockAxiosInstance.delete).toHaveBeenCalledWith(`/filaments/${filamentId}`);
      expect(result).toEqual(mockResponse);
    });

    it('should update bulk sale', async () => {
      const saleData = {
        filamentIds: ['1', '2', '3'],
        salePercentage: 20,
        saleStartDate: '2024-01-01',
        saleEndDate: '2024-01-31',
        enableSale: true
      };
      const mockResponse = { updated: 3, success: true };
      mockAxiosInstance.post.mockResolvedValue({ data: mockResponse });

      const result = await filamentService.updateBulkSale(saleData);

      expect(mockAxiosInstance.post).toHaveBeenCalledWith('/filaments/sale/bulk', saleData);
      expect(result).toEqual(mockResponse);
    });
  });

  describe('Interceptors', () => {
    it('should have interceptors configured', () => {
      expect(mockAxiosInstance.interceptors).toBeDefined();
      expect(mockAxiosInstance.interceptors.request).toBeDefined();
      expect(mockAxiosInstance.interceptors.response).toBeDefined();
    });

    it('should have request interceptor set up', () => {
      const mockRequestUse = mockAxiosInstance.interceptors.request.use;
      expect(mockRequestUse).toBeDefined();
    });

    it('should have response interceptor set up', () => {
      const mockResponseUse = mockAxiosInstance.interceptors.response.use;
      expect(mockResponseUse).toBeDefined();
    });
  });

  describe('API configuration', () => {
    it('should export the configured axios instance', () => {
      expect(api).toBe(mockAxiosInstance);
    });

    it('should have axios instance defined', () => {
      expect(mockAxiosInstance).toBeDefined();
    });
  });
});