import { MediaReference } from '@core/domain/media/MediaReference';
import { Game } from '@core/racing/domain/entities/Game';
import { League } from '@core/racing/domain/entities/League';
import { LeagueScoringConfig } from '@core/racing/domain/entities/LeagueScoringConfig';
import { Season } from '@core/racing/domain/entities/season/Season';
import type { LeagueScoringPreset } from '@core/racing/domain/types/LeagueScoringPreset';
import { PointsTable } from '@core/racing/domain/value-objects/PointsTable';
import { beforeEach, describe, expect, it } from 'vitest';
import { AllLeaguesWithCapacityAndScoringPresenter } from './AllLeaguesWithCapacityAndScoringPresenter';

describe('AllLeaguesWithCapacityAndScoringPresenter', () => {
  let presenter: AllLeaguesWithCapacityAndScoringPresenter;

  beforeEach(() => {
    presenter = new AllLeaguesWithCapacityAndScoringPresenter();
  });

  describe('present', () => {
    it('should map empty leagues array to DTO', async () => {
      await presenter.present({ leagues: [] });

      const vm = presenter.getViewModel();

      expect(vm).toEqual({
        leagues: [],
        totalCount: 0,
      });
    });

    it('should map leagues with basic information', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: {
          pointsSystem: 'f1-2024',
          maxDrivers: 20,
        },
        createdAt: new Date('2024-01-01T00:00:00Z'),
      });

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 5,
            maxDrivers: 20,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues).toHaveLength(1);
      expect(vm.leagues[0]!.id).toBe('league-1');
      expect(vm.leagues[0]!.name).toBe('Test League');
      expect(vm.leagues[0]!.description).toBe('A test league');
      expect(vm.leagues[0]!.ownerId).toBe('owner-1');
      expect(vm.leagues[0]!.createdAt).toBe('2024-01-01T00:00:00.000Z');
      expect(vm.leagues[0]!.settings.maxDrivers).toBe(20);
      expect(vm.leagues[0]!.usedSlots).toBe(5);
      expect(vm.totalCount).toBe(1);
    });

    it('should map leagues with social links', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024' },
        socialLinks: {
          discordUrl: 'https://discord.gg/test',
          youtubeUrl: 'https://youtube.com/test',
          websiteUrl: 'https://test.com',
        },
      });

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 3,
            maxDrivers: 32,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues[0]!.socialLinks).toEqual({
        discordUrl: 'https://discord.gg/test',
        youtubeUrl: 'https://youtube.com/test',
        websiteUrl: 'https://test.com',
      });
    });

    it('should map leagues with category', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024' },
        category: 'Formula',
      });

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 2,
            maxDrivers: 32,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues[0]!.category).toBe('Formula');
    });

    it('should map leagues with session duration and qualifying format', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: {
          pointsSystem: 'f1-2024',
          sessionDuration: 90,
          qualifyingFormat: 'single-lap',
        },
      });

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 4,
            maxDrivers: 32,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues[0]!.settings.sessionDuration).toBe(90);
      expect(vm.leagues[0]!.settings.qualifyingFormat).toBe('single-lap');
    });

    it('should map leagues with timing summary for hours', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024' },
      });

      const season = Season.create({
        id: 'season-1',
        leagueId: 'league-1',
        gameId: 'iracing',
        name: 'Season 1',
        status: 'active',
        schedule: {
          startDate: new Date('2024-01-01'),
          timeOfDay: { hour: 20, minute: 0 },
        } as never,
      } as never);

      const scoringConfig = LeagueScoringConfig.create({
        id: 'scoring-1',
        seasonId: 'season-1',
        scoringPresetId: 'preset-1',
        championships: [
          {
            id: 'champ-1',
            name: 'Drivers Championship',
            type: 'driver',
            sessionTypes: ['main'],
            pointsTableBySessionType: {
              practice: new PointsTable({}),
              qualifying: new PointsTable({}),
              q1: new PointsTable({}),
              q2: new PointsTable({}),
              q3: new PointsTable({}),
              sprint: new PointsTable({}),
              main: new PointsTable({ 1: 25, 2: 18, 3: 15, 4: 12, 5: 10, 6: 8, 7: 6, 8: 4, 9: 2, 10: 1 }),
              timeTrial: new PointsTable({}),
            },
            dropScorePolicy: { strategy: 'bestNResults', count: 3 },
          },
        ],
      });

      const game = Game.create({
        id: 'iracing',
        name: 'iRacing',
      });

      const preset: LeagueScoringPreset = {
        id: 'preset-1',
        name: 'Test Preset',
        description: 'Test preset description',
        primaryChampionshipType: 'driver',
        dropPolicySummary: 'Best 8 of 12',
        sessionSummary: 'Qualifying + Race',
        bonusSummary: 'Bonus points for top 3',
        defaultTimings: {
          mainRaceMinutes: 120,
          practiceMinutes: 30,
          qualifyingMinutes: 20,
          sprintRaceMinutes: 0,
          sessionCount: 1
        },
      };

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 5,
            maxDrivers: 32,
            season,
            scoringConfig,
            game,
            preset,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues[0]!.timingSummary).toBe('2h Race');
      expect(vm.leagues[0]!.scoring).toEqual({
        gameId: 'iracing',
        gameName: 'iRacing',
        primaryChampionshipType: 'driver',
        scoringPresetId: 'preset-1',
        scoringPresetName: 'Test Preset',
        dropPolicySummary: 'Best 8 of 12',
        scoringPatternSummary: 'Qualifying + Race',
      });
    });

    it('should map leagues with timing summary for minutes', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024' },
      });

      const season = Season.create({
        id: 'season-1',
        leagueId: 'league-1',
        gameId: 'iracing',
        name: 'Season 1',
        status: 'active',
        schedule: {
          startDate: new Date('2024-01-01'),
          timeOfDay: { hour: 20, minute: 0 },
        } as never,
      } as never);

      const scoringConfig = LeagueScoringConfig.create({
        id: 'scoring-1',
        seasonId: 'season-1',
        scoringPresetId: 'preset-1',
        championships: [
          {
            id: 'champ-1',
            name: 'Drivers Championship',
            type: 'driver',
            sessionTypes: ['main'],
            pointsTableBySessionType: {
              practice: new PointsTable({}),
              qualifying: new PointsTable({}),
              q1: new PointsTable({}),
              q2: new PointsTable({}),
              q3: new PointsTable({}),
              sprint: new PointsTable({}),
              main: new PointsTable({ 1: 25, 2: 18, 3: 15, 4: 12, 5: 10, 6: 8, 7: 6, 8: 4, 9: 2, 10: 1 }),
              timeTrial: new PointsTable({}),
            },
            dropScorePolicy: { strategy: 'bestNResults', count: 3 },
          },
        ],
      });

      const game = Game.create({
        id: 'iracing',
        name: 'iRacing',
      });

      const preset: LeagueScoringPreset = {
        id: 'preset-1',
        name: 'Test Preset',
        description: 'Test preset description',
        primaryChampionshipType: 'driver',
        dropPolicySummary: 'Best 8 of 12',
        sessionSummary: 'Qualifying + Race',
        bonusSummary: 'Bonus points for top 3',
        defaultTimings: {
          mainRaceMinutes: 45,
          practiceMinutes: 15,
          qualifyingMinutes: 10,
          sprintRaceMinutes: 0,
          sessionCount: 1
        },
      };

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 5,
            maxDrivers: 32,
            season,
            scoringConfig,
            game,
            preset,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues[0]!.timingSummary).toBe('45m Race');
    });

    it('should handle leagues without scoring information', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024' },
      });

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 5,
            maxDrivers: 32,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues[0]!.scoring).toBeUndefined();
      expect(vm.leagues[0]!.timingSummary).toBeUndefined();
    });

    it('should handle leagues with system default logoRef', async () => {
      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024' },
      });

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 5,
            maxDrivers: 32,
          },
        ],
      });

      const vm = presenter.getViewModel();

      // Should be null when it's system default (as per presenter logic)
      expect(vm.leagues[0]!.logoUrl).toBeNull();
    });

    it('should handle multiple leagues with different configurations', async () => {
      const league1 = League.create({
        id: 'league-1',
        name: 'League One',
        description: 'First league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024', maxDrivers: 20 },
        category: 'Formula',
      });

      const league2 = League.create({
        id: 'league-2',
        name: 'League Two',
        description: 'Second league',
        ownerId: 'owner-2',
        settings: { pointsSystem: 'indycar', maxDrivers: 40 },
        socialLinks: { discordUrl: 'https://discord.gg/league2' },
      });

      await presenter.present({
        leagues: [
          {
            league: league1,
            currentDrivers: 10,
            maxDrivers: 20,
          },
          {
            league: league2,
            currentDrivers: 25,
            maxDrivers: 40,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues).toHaveLength(2);
      expect(vm.totalCount).toBe(2);
      expect(vm.leagues[0]!.id).toBe('league-1');
      expect(vm.leagues[0]!.category).toBe('Formula');
      expect(vm.leagues[0]!.settings.maxDrivers).toBe(20);
      expect(vm.leagues[1]!.id).toBe('league-2');
      expect(vm.leagues[1]!.socialLinks?.discordUrl).toBe('https://discord.gg/league2');
      expect(vm.leagues[1]!.settings.maxDrivers).toBe(40);
    });
  });

  describe('getViewModel', () => {
    it('should throw error when not presented', () => {
      expect(() => presenter.getViewModel()).toThrow('Presenter not presented');
    });

    it('should return the model when presented', async () => {
      await presenter.present({ leagues: [] });

      const vm = presenter.getViewModel();

      expect(vm).toEqual({
        leagues: [],
        totalCount: 0,
      });
    });
  });

  describe('setMediaResolver', () => {
    it('should use media resolver to resolve logo URLs', async () => {
      const mockResolver = {
        resolve: async () => 'https://cdn.example.com/league-logo.png',
      };

      presenter.setMediaResolver(mockResolver);

      const league = League.create({
        id: 'league-1',
        name: 'Test League',
        description: 'A test league',
        ownerId: 'owner-1',
        settings: { pointsSystem: 'f1-2024' },
      });

      // Override logoRef to uploaded type
      (league as unknown as { logoRef: unknown }).logoRef = MediaReference.fromJSON({
        type: 'uploaded',
        mediaId: 'media-123',
      });

      await presenter.present({
        leagues: [
          {
            league,
            currentDrivers: 5,
            maxDrivers: 32,
          },
        ],
      });

      const vm = presenter.getViewModel();

      expect(vm.leagues[0]!.logoUrl).toBe('https://cdn.example.com/league-logo.png');
    });
  });
});