import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import {
  GetTeamMembershipUseCase,
  GetTeamMembershipInput,
  GetTeamMembershipErrorCode,
} from './GetTeamMembershipUseCase';

describe('GetTeamMembershipUseCase', () => {
  const mockGetMembership = vi.fn();
  const mockMembershipRepo: {
    getMembership: Mock;
    getActiveMembershipForDriver: Mock;
    getTeamMembers: Mock;
    saveMembership: Mock;
    removeMembership: Mock;
    getJoinRequests: Mock;
    countByTeamId: Mock;
    saveJoinRequest: Mock;
    removeJoinRequest: Mock;
  } = {
    getMembership: mockGetMembership,
    getActiveMembershipForDriver: vi.fn(),
    getTeamMembers: vi.fn(),
    saveMembership: vi.fn(),
    removeMembership: vi.fn(),
    getJoinRequests: vi.fn(),
    countByTeamId: vi.fn(),
    saveJoinRequest: vi.fn(),
    removeJoinRequest: vi.fn(),
  };

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

  let useCase: GetTeamMembershipUseCase;

  beforeEach(() => {
    vi.clearAllMocks();
    useCase = new GetTeamMembershipUseCase(mockMembershipRepo as unknown as TeamMembershipRepository, mockLogger);
  });

  it('should return membership data when membership exists', async () => {
    const teamId = 'team1';
    const driverId = 'driver1';
    const membership = {
      teamId,
      driverId,
      role: 'manager' as const,
      status: 'active' as const,
      joinedAt: new Date('2023-01-01'),
    };

    mockGetMembership.mockResolvedValue(membership);

    const input: GetTeamMembershipInput = { teamId, driverId };

    const result = await useCase.execute(input);

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

    expect(presented.membership).toEqual({
      role: 'manager',
      joinedAt: '2023-01-01T00:00:00.000Z',
      isActive: true,
    });
  });

  it('should return null membership when no membership exists', async () => {
    const teamId = 'team1';
    const driverId = 'driver1';

    mockGetMembership.mockResolvedValue(null);

    const input: GetTeamMembershipInput = { teamId, driverId };

    const result = await useCase.execute(input);

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

    expect(presented.membership).toBeNull();
  });

  it('should map driver role to member', async () => {
    const teamId = 'team1';
    const driverId = 'driver1';
    const membership = {
      teamId,
      driverId,
      role: 'driver' as const,
      status: 'active' as const,
      joinedAt: new Date('2023-01-01'),
    };

    mockGetMembership.mockResolvedValue(membership);

    const input: GetTeamMembershipInput = { teamId, driverId };

    const result = await useCase.execute(input);

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

    expect(presented.membership?.role).toBe('member');
  });

  it('should return error when repository throws', async () => {
    const teamId = 'team1';
    const driverId = 'driver1';
    const error = new Error('Repository error');

    mockGetMembership.mockRejectedValue(error);

    const input: GetTeamMembershipInput = { teamId, driverId };

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);

    const err = result.unwrapErr() as ApplicationErrorCode<
      GetTeamMembershipErrorCode,
      { message: string }
    >;

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