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 type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import {
    RejectTeamJoinRequestUseCase,
    type RejectTeamJoinRequestErrorCode,
    type RejectTeamJoinRequestInput,
} from './RejectTeamJoinRequestUseCase';

interface TeamRepositoryMock {
  findById: Mock;
}

interface TeamMembershipRepositoryMock {
  getMembership: Mock;
  getJoinRequests: Mock;
  removeJoinRequest: Mock;
}

describe('RejectTeamJoinRequestUseCase', () => {
  let teamRepository: TeamRepositoryMock;
  let membershipRepository: TeamMembershipRepositoryMock;
  let logger: Logger & { info: Mock; warn: Mock; error: Mock; debug: Mock };
  let useCase: RejectTeamJoinRequestUseCase;

  beforeEach(() => {
    teamRepository = {
      findById: vi.fn(),
    };

    membershipRepository = {
      getMembership: vi.fn(),
      getJoinRequests: vi.fn(),
      removeJoinRequest: vi.fn(),
    };

    logger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    } as unknown as Logger & { info: Mock; warn: Mock; error: Mock; debug: Mock };

    useCase = new RejectTeamJoinRequestUseCase(teamRepository as unknown as TeamRepository,
      membershipRepository as unknown as TeamMembershipRepository,
      logger);
  });

  it('rejects a pending join request successfully and presents result', async () => {
    const input: RejectTeamJoinRequestInput = {
      teamId: 'team-1',
      managerId: 'manager-1',
      requestId: 'req-1',
    };

    teamRepository.findById.mockResolvedValue({ id: 'team-1' });
    membershipRepository.getMembership.mockResolvedValue({
      teamId: 'team-1',
      driverId: 'manager-1',
      role: 'owner',
      status: 'active',
      joinedAt: new Date(),
    });
    membershipRepository.getJoinRequests.mockResolvedValue([
      {
        id: 'req-1',
        teamId: 'team-1',
        driverId: 'driver-1',
        requestedAt: new Date(),
        message: 'please',
        status: 'pending',
      },
    ]);
    membershipRepository.removeJoinRequest.mockResolvedValue(undefined);

    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();
    expect(presented.teamId).toBe('team-1');
    expect(presented.requestId).toBe('req-1');
    expect(presented.status).toBe('rejected');
    expect(membershipRepository.removeJoinRequest).toHaveBeenCalledWith('req-1');
  });

  it('returns TEAM_NOT_FOUND when team does not exist', async () => {
    const input: RejectTeamJoinRequestInput = {
      teamId: 'missing-team',
      managerId: 'manager-1',
      requestId: 'req-1',
    };

    teamRepository.findById.mockResolvedValue(null);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr() as ApplicationErrorCode<
      RejectTeamJoinRequestErrorCode,
      { message: string }
    >;
    expect(err.code).toBe('TEAM_NOT_FOUND');
    expect(membershipRepository.removeJoinRequest).not.toHaveBeenCalled();
  });

  it('returns UNAUTHORIZED when manager is not authorized', async () => {
    const input: RejectTeamJoinRequestInput = {
      teamId: 'team-1',
      managerId: 'user-1',
      requestId: 'req-1',
    };

    teamRepository.findById.mockResolvedValue({ id: 'team-1' });
    membershipRepository.getMembership.mockResolvedValue(null);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr() as ApplicationErrorCode<
      RejectTeamJoinRequestErrorCode,
      { message: string }
    >;
    expect(err.code).toBe('UNAUTHORIZED');
    expect(membershipRepository.removeJoinRequest).not.toHaveBeenCalled();
  });

  it('returns REQUEST_NOT_FOUND when join request does not exist', async () => {
    const input: RejectTeamJoinRequestInput = {
      teamId: 'team-1',
      managerId: 'manager-1',
      requestId: 'missing-req',
    };

    teamRepository.findById.mockResolvedValue({ id: 'team-1' });
    membershipRepository.getMembership.mockResolvedValue({
      teamId: 'team-1',
      driverId: 'manager-1',
      role: 'owner',
      status: 'active',
      joinedAt: new Date(),
    });
    membershipRepository.getJoinRequests.mockResolvedValue([]);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr() as ApplicationErrorCode<
      RejectTeamJoinRequestErrorCode,
      { message: string }
    >;
    expect(err.code).toBe('REQUEST_NOT_FOUND');
    expect(membershipRepository.removeJoinRequest).not.toHaveBeenCalled();
  });

  it('returns INVALID_REQUEST_STATE when join request is not pending', async () => {
    const input: RejectTeamJoinRequestInput = {
      teamId: 'team-1',
      managerId: 'manager-1',
      requestId: 'req-1',
    };

    teamRepository.findById.mockResolvedValue({ id: 'team-1' });
    membershipRepository.getMembership.mockResolvedValue({
      teamId: 'team-1',
      driverId: 'manager-1',
      role: 'owner',
      status: 'active',
      joinedAt: new Date(),
    });
    membershipRepository.getJoinRequests.mockResolvedValue([
      {
        id: 'req-1',
        teamId: 'team-1',
        driverId: 'driver-1',
        requestedAt: new Date(),
        message: 'please',
        status: 'approved',
      },
    ]);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr() as ApplicationErrorCode<
      RejectTeamJoinRequestErrorCode,
      { message: string }
    >;
    expect(err.code).toBe('INVALID_REQUEST_STATE');
    expect(membershipRepository.removeJoinRequest).not.toHaveBeenCalled();
  });

  it('returns REPOSITORY_ERROR when repository throws', async () => {
    const input: RejectTeamJoinRequestInput = {
      teamId: 'team-1',
      managerId: 'manager-1',
      requestId: 'req-1',
    };

    const repoError = new Error('Repository failure');
    teamRepository.findById.mockRejectedValue(repoError);

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr() as ApplicationErrorCode<
      RejectTeamJoinRequestErrorCode,
      { message: string }
    >;
    expect(err.code).toBe('REPOSITORY_ERROR');
    expect(err.details.message).toBe('Repository failure');
    expect(membershipRepository.removeJoinRequest).not.toHaveBeenCalled();
  });
});