/**
 * Integration Tests for LeaguesPageQuery
 *
 * Tests the LeaguesPageQuery with mocked API clients to verify:
 * - Happy path: API returns valid leagues data
 * - Error handling: 404 when leagues endpoint not found
 * - Error handling: 500 when API server error
 * - Empty results: API returns empty leagues list
 */

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { LeaguesPageQuery } from '@/lib/page-queries/LeaguesPageQuery';

// Mock data factories
const createMockLeaguesData = () => ({
  leagues: [
    {
      id: 'league-1',
      name: 'Test League 1',
      description: 'A test league',
      ownerId: 'driver-1',
      createdAt: new Date().toISOString(),
      usedSlots: 5,
      settings: {
        maxDrivers: 10,
      },
      scoring: {
        gameId: 'game-1',
        gameName: 'Test Game',
        primaryChampionshipType: 'driver' as const,
        scoringPresetId: 'preset-1',
        scoringPresetName: 'Test Preset',
        dropPolicySummary: 'No drops',
        scoringPatternSummary: 'Standard scoring',
      },
    },
    {
      id: 'league-2',
      name: 'Test League 2',
      description: 'Another test league',
      ownerId: 'driver-2',
      createdAt: new Date().toISOString(),
      usedSlots: 15,
      settings: {
        maxDrivers: 20,
      },
      scoring: {
        gameId: 'game-1',
        gameName: 'Test Game',
        primaryChampionshipType: 'driver' as const,
        scoringPresetId: 'preset-1',
        scoringPresetName: 'Test Preset',
        dropPolicySummary: 'No drops',
        scoringPatternSummary: 'Standard scoring',
      },
    },
  ],
  totalCount: 2,
});

const createMockEmptyLeaguesData = () => ({
  leagues: [],
});

describe('LeaguesPageQuery Integration', () => {
  let originalFetch: typeof global.fetch;

  beforeEach(() => {
    // Store original fetch to restore later
    originalFetch = global.fetch;
  });

  afterEach(() => {
    // Restore original fetch
    global.fetch = originalFetch;
  });

  describe('Happy Path', () => {
    it('should return valid leagues data when API returns success', async () => {
      // Arrange
      const mockData = createMockLeaguesData();
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => mockData,
        text: async () => JSON.stringify(mockData),
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isOk()).toBe(true);
      const viewData = result.unwrap();
      
      expect(viewData).toBeDefined();
      expect(viewData.leagues).toBeDefined();
      expect(viewData.leagues.length).toBe(2);
      
      // Verify first league
      expect(viewData.leagues[0].id).toBe('league-1');
      expect(viewData.leagues[0].name).toBe('Test League 1');
      expect(viewData.leagues[0].settings.maxDrivers).toBe(10);
      expect(viewData.leagues[0].usedSlots).toBe(5);
      
      // Verify second league
      expect(viewData.leagues[1].id).toBe('league-2');
      expect(viewData.leagues[1].name).toBe('Test League 2');
      expect(viewData.leagues[1].settings.maxDrivers).toBe(20);
      expect(viewData.leagues[1].usedSlots).toBe(15);
    });

    it('should handle single league correctly', async () => {
      // Arrange
      const mockData = {
        leagues: [
          {
            id: 'single-league',
            name: 'Single League',
            description: 'Only one league',
            ownerId: 'driver-1',
            createdAt: new Date().toISOString(),
            usedSlots: 3,
            settings: {
              maxDrivers: 5,
            },
            scoring: {
              gameId: 'game-1',
              gameName: 'Test Game',
              primaryChampionshipType: 'driver' as const,
              scoringPresetId: 'preset-1',
              scoringPresetName: 'Test Preset',
              dropPolicySummary: 'No drops',
              scoringPatternSummary: 'Standard scoring',
            },
          },
        ],
      };
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => mockData,
        text: async () => JSON.stringify(mockData),
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isOk()).toBe(true);
      const viewData = result.unwrap();
      
      expect(viewData.leagues.length).toBe(1);
      expect(viewData.leagues[0].id).toBe('single-league');
      expect(viewData.leagues[0].name).toBe('Single League');
    });
  });

  describe('Empty Results', () => {
    it('should handle empty leagues list from API', async () => {
      // Arrange
      const mockData = createMockEmptyLeaguesData();
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => mockData,
        text: async () => JSON.stringify(mockData),
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isOk()).toBe(true);
      const viewData = result.unwrap();
      
      expect(viewData).toBeDefined();
      expect(viewData.leagues).toBeDefined();
      expect(viewData.leagues.length).toBe(0);
    });
  });

  describe('Error Handling', () => {
    it('should handle 404 error when leagues endpoint not found', async () => {
      // Arrange
      global.fetch = vi.fn().mockResolvedValue({
        ok: false,
        status: 404,
        statusText: 'Not Found',
        text: async () => 'Leagues not found',
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('LEAGUES_FETCH_FAILED');
    });

    it('should handle 500 error when API server error', async () => {
      // Arrange
      global.fetch = vi.fn().mockResolvedValue({
        ok: false,
        status: 500,
        statusText: 'Internal Server Error',
        text: async () => 'Internal Server Error',
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('LEAGUES_FETCH_FAILED');
    });

    it('should handle network error', async () => {
      // Arrange
      global.fetch = vi.fn().mockRejectedValue(new Error('Network error: Unable to reach the API server'));
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('LEAGUES_FETCH_FAILED');
    });

    it('should handle timeout error', async () => {
      // Arrange
      const timeoutError = new Error('Request timed out after 30 seconds');
      timeoutError.name = 'AbortError';
      global.fetch = vi.fn().mockRejectedValue(timeoutError);
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('LEAGUES_FETCH_FAILED');
    });

    it('should handle unauthorized error (redirect)', async () => {
      // Arrange
      global.fetch = vi.fn().mockResolvedValue({
        ok: false,
        status: 401,
        statusText: 'Unauthorized',
        text: async () => 'Unauthorized',
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('redirect');
    });

    it('should handle forbidden error (redirect)', async () => {
      // Arrange
      global.fetch = vi.fn().mockResolvedValue({
        ok: false,
        status: 403,
        statusText: 'Forbidden',
        text: async () => 'Forbidden',
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('redirect');
    });

    it('should handle unknown error type', async () => {
      // Arrange
      global.fetch = vi.fn().mockResolvedValue({
        ok: false,
        status: 999,
        statusText: 'Unknown Error',
        text: async () => 'Unknown error',
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('UNKNOWN_ERROR');
    });
  });

  describe('Edge Cases', () => {
    it('should handle API returning null or undefined data', async () => {
      // Arrange
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => null,
        text: async () => 'null',
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('LEAGUES_FETCH_FAILED');
    });

    it('should handle API returning malformed data', async () => {
      // Arrange
      const mockData = {
        // Missing 'leagues' property
        someOtherProperty: 'value',
      };
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => mockData,
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      expect(result.isErr()).toBe(true);
      const error = result.getError();
      expect(error).toBe('LEAGUES_FETCH_FAILED');
    });

    it('should handle API returning leagues with missing required fields', async () => {
      // Arrange
      const mockData = {
        leagues: [
          {
            id: 'league-1',
            name: 'Test League',
            // Missing other required fields
          },
        ],
      };
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        json: async () => mockData,
      });
      
      // Act
      const result = await LeaguesPageQuery.execute();

      // Assert
      // Should still succeed - the builder should handle partial data
      expect(result.isOk()).toBe(true);
      const viewData = result.unwrap();
      expect(viewData.leagues.length).toBe(1);
    });
  });
});
