import { Test, TestingModule } from '@nestjs/testing';
import { TeamService } from './TeamService';
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
import type { Logger } from '@core/shared/application/Logger';
import { AllTeamsPresenter } from './presenters/AllTeamsPresenter';
import { DriverTeamPresenter } from './presenters/DriverTeamPresenter';
import { AllTeamsViewModel, DriverTeamViewModel, GetDriverTeamQuery } from './dto/TeamDto';
import { TEAM_GET_ALL_USE_CASE_TOKEN, TEAM_GET_DRIVER_TEAM_USE_CASE_TOKEN, TEAM_LOGGER_TOKEN } from './TeamProviders';

describe('TeamService', () => {
  let service: TeamService;
  let getAllTeamsUseCase: jest.Mocked<GetAllTeamsUseCase>;
  let getDriverTeamUseCase: jest.Mocked<GetDriverTeamUseCase>;
  let logger: jest.Mocked<Logger>;

  beforeEach(async () => {
    const mockGetAllTeamsUseCase = {
      execute: jest.fn(),
    };
    const mockGetDriverTeamUseCase = {
      execute: jest.fn(),
    };
    const mockLogger = {
      debug: jest.fn(),
      info: jest.fn(),
      error: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        TeamService,
        {
          provide: TEAM_GET_ALL_USE_CASE_TOKEN,
          useValue: mockGetAllTeamsUseCase,
        },
        {
          provide: TEAM_GET_DRIVER_TEAM_USE_CASE_TOKEN,
          useValue: mockGetDriverTeamUseCase,
        },
        {
          provide: TEAM_LOGGER_TOKEN,
          useValue: mockLogger,
        },
      ],
    }).compile();

    service = module.get<TeamService>(TeamService);
    getAllTeamsUseCase = module.get(TEAM_GET_ALL_USE_CASE_TOKEN);
    getDriverTeamUseCase = module.get(TEAM_GET_DRIVER_TEAM_USE_CASE_TOKEN);
    logger = module.get(TEAM_LOGGER_TOKEN);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('getAllTeams', () => {
    it('should create presenter, call use case, and return view model', async () => {
      const mockViewModel: AllTeamsViewModel = {
        teams: [],
        totalCount: 0,
      };

      const mockPresenter = {
        reset: jest.fn(),
        present: jest.fn(),
        get viewModel(): AllTeamsViewModel {
          return mockViewModel;
        },
      };

      // Mock the presenter constructor
      const originalConstructor = AllTeamsPresenter;
      (AllTeamsPresenter as any) = jest.fn().mockImplementation(() => mockPresenter);

      // Mock the use case to call the presenter
      getAllTeamsUseCase.execute.mockImplementation(async (input, presenter) => {
        presenter.present({ teams: [] });
      });

      const result = await service.getAllTeams();

      expect(AllTeamsPresenter).toHaveBeenCalled();
      expect(getAllTeamsUseCase.execute).toHaveBeenCalledWith(undefined, mockPresenter);
      expect(result).toBe(mockViewModel);

      // Restore
      AllTeamsPresenter = originalConstructor;
    });
  });

  describe('getDriverTeam', () => {
    it('should create presenter, call use case, and return view model', async () => {
      const query: GetDriverTeamQuery = { teamId: 'team1', driverId: 'driver1' };
      const mockViewModel: DriverTeamViewModel = {
        team: {
          id: 'team1',
          name: 'Team 1',
          tag: 'T1',
          description: 'Description',
          ownerId: 'driver1',
          leagues: [],
        },
        membership: {
          role: 'owner' as any,
          joinedAt: new Date(),
          isActive: true,
        },
        isOwner: true,
        canManage: true,
      };

      const mockPresenter = {
        reset: jest.fn(),
        present: jest.fn(),
        get viewModel(): DriverTeamViewModel {
          return mockViewModel;
        },
      };

      // Mock the presenter constructor
      const originalConstructor = DriverTeamPresenter;
      (DriverTeamPresenter as any) = jest.fn().mockImplementation(() => mockPresenter);

      // Mock the use case to call the presenter
      getDriverTeamUseCase.execute.mockImplementation(async (input, presenter) => {
        presenter.present({
          team: {
            id: 'team1',
            name: 'Team 1',
            tag: 'T1',
            description: 'Description',
            ownerId: 'driver1',
            leagues: [],
          },
          membership: {
            role: 'owner',
            status: 'active',
            joinedAt: new Date(),
          },
          driverId: 'driver1',
        });
      });

      const result = await service.getDriverTeam(query);

      expect(DriverTeamPresenter).toHaveBeenCalled();
      expect(getDriverTeamUseCase.execute).toHaveBeenCalledWith({ driverId: 'driver1' }, mockPresenter);
      expect(result).toBe(mockViewModel);

      // Restore
      DriverTeamPresenter = originalConstructor;
    });

    it('should return null on error', async () => {
      const query: GetDriverTeamQuery = { teamId: 'team1', driverId: 'driver1' };

      // Mock the use case to throw an error
      getDriverTeamUseCase.execute.mockRejectedValue(new Error('Team not found'));

      const result = await service.getDriverTeam(query);

      expect(result).toBeNull();
      expect(logger.error).toHaveBeenCalled();
    });
  });
});