import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamStatsRepository } from '../../domain/repositories/TeamStatsRepository';
import { GetAllTeamsUseCase, type GetAllTeamsInput } from './GetAllTeamsUseCase';

describe('GetAllTeamsUseCase', () => {
  const mockTeamFindAll = vi.fn();
  const mockTeamRepo: TeamRepository = {
    findById: vi.fn(),
    findAll: mockTeamFindAll,
    findByLeagueId: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    exists: vi.fn(),
  };

  const mockTeamMembershipCountByTeamId = vi.fn();
  const mockTeamMembershipRepo: TeamMembershipRepository = {
    getMembership: vi.fn(),
    getActiveMembershipForDriver: vi.fn(),
    getTeamMembers: vi.fn(),
    saveMembership: vi.fn(),
    removeMembership: vi.fn(),
    countByTeamId: mockTeamMembershipCountByTeamId,
    getJoinRequests: vi.fn(),
    saveJoinRequest: vi.fn(),
    removeJoinRequest: vi.fn(),
  };

  const mockTeamStatsRepo: TeamStatsRepository = {
    getTeamStats: vi.fn(),
    saveTeamStats: vi.fn(),
    getAllStats: vi.fn(),
    clear: vi.fn(),
  };

  const mockLogger: Logger = {
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should return teams data', async () => {
    const useCase = new GetAllTeamsUseCase(mockTeamRepo,
      mockTeamMembershipRepo,
      mockTeamStatsRepo,
      mockLogger);

    const team1 = {
      id: { toString: () => 'team1' },
      name: { toString: () => 'Team One' },
      tag: { toString: () => 'TO' },
      description: { toString: () => 'Description One' },
      ownerId: { toString: () => 'owner1' },
      leagues: [{ toString: () => 'league1' }],
      createdAt: { toDate: () => new Date('2023-01-01T00:00:00Z') },
      logoRef: { toJSON: () => ({ type: 'generated', generationRequestId: 'team-team1' }) },
      category: undefined,
      isRecruiting: false,
    };
    const team2 = {
      id: { toString: () => 'team2' },
      name: { toString: () => 'Team Two' },
      tag: { toString: () => 'TT' },
      description: { toString: () => 'Description Two' },
      ownerId: { toString: () => 'owner2' },
      leagues: [{ toString: () => 'league2' }],
      createdAt: { toDate: () => new Date('2023-01-02T00:00:00Z') },
      logoRef: { toJSON: () => ({ type: 'generated', generationRequestId: 'team-team2' }) },
      category: undefined,
      isRecruiting: true,
    };

    mockTeamFindAll.mockResolvedValue([team1, team2]);
    mockTeamMembershipCountByTeamId.mockImplementation((id: string) => Promise.resolve(id === 'team1' ? 5 : 3));

    (mockTeamStatsRepo.getTeamStats as unknown as Mock).mockImplementation((teamId: string) =>
      Promise.resolve(
        teamId === 'team1'
          ? {
              performanceLevel: 'intermediate',
              specialization: 'mixed',
              region: 'EU',
              languages: ['en'],
              totalWins: 2,
              totalRaces: 10,
              rating: 1200,
            }
          : {
              performanceLevel: 'advanced',
              specialization: 'mixed',
              region: 'US',
              languages: ['en', 'de'],
              totalWins: 5,
              totalRaces: 20,
              rating: 1400,
            },
      ),
    );

    const result = await useCase.execute({} as GetAllTeamsInput);

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();

    expect(presented.totalCount).toBe(2);
    expect(presented.teams[0].team).toBe(team1);
    expect(presented.teams[0].memberCount).toBe(5);
    expect(presented.teams[0].rating).toBe(1200);
    expect(presented.teams[1].team).toBe(team2);
    expect(presented.teams[1].memberCount).toBe(3);
    expect(presented.teams[1].rating).toBe(1400);
  });

  it('should return empty result when no teams', async () => {
    const useCase = new GetAllTeamsUseCase(mockTeamRepo,
      mockTeamMembershipRepo,
      mockTeamStatsRepo,
      mockLogger);

    mockTeamFindAll.mockResolvedValue([]);

    const result = await useCase.execute({} as GetAllTeamsInput);

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();

    expect(presented.teams).toEqual([]);
    expect(presented.totalCount).toBe(0);
  });

  it('should return error when repository throws', async () => {
    const useCase = new GetAllTeamsUseCase(mockTeamRepo,
      mockTeamMembershipRepo,
      mockTeamStatsRepo,
      mockLogger);

    const error = new Error('Repository error');
    mockTeamFindAll.mockRejectedValue(error);

    const result = await useCase.execute({} as GetAllTeamsInput);

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr();

    expect(err.code).toBe('REPOSITORY_ERROR');
    expect(err.details.message).toBe('Repository error');
  });
});
