import type { ITeamRepository } from '../../domain/repositories/ITeamRepository';
import type { ITeamMembershipRepository } from '../../domain/repositories/ITeamMembershipRepository';
import type {
  TeamMembership,
  TeamMembershipStatus,
  TeamRole,
} from '../../domain/types/TeamMembership';
import type { JoinTeamCommandDTO } from '../dtos/JoinTeamCommandDTO';
import type { AsyncUseCase, Logger } from '@core/shared/application';
import { Result as SharedResult } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

type JoinTeamErrorCode = 'ALREADY_IN_TEAM' | 'ALREADY_MEMBER' | 'TEAM_NOT_FOUND' | 'REPOSITORY_ERROR';

type JoinTeamApplicationError = ApplicationErrorCode<JoinTeamErrorCode, { message: string }>;

export class JoinTeamUseCase implements AsyncUseCase<JoinTeamCommandDTO, void, JoinTeamErrorCode> {
  constructor(
    private readonly teamRepository: ITeamRepository,
    private readonly membershipRepository: ITeamMembershipRepository,
    private readonly logger: Logger,
  ) {}

  async execute(command: JoinTeamCommandDTO): Promise<SharedResult<void, JoinTeamApplicationError>> {
    this.logger.debug('Attempting to join team', { command });
    const { teamId, driverId } = command;

    try {
      const existingActive = await this.membershipRepository.getActiveMembershipForDriver(
        driverId,
      );
      if (existingActive) {
        this.logger.warn('Driver already belongs to a team', { driverId, teamId });
        return SharedResult.err({ code: 'ALREADY_IN_TEAM', details: { message: 'Driver already belongs to a team' } });
      }

      const existingMembership = await this.membershipRepository.getMembership(teamId, driverId);
      if (existingMembership) {
        this.logger.warn('Driver already has a pending or active membership request', { driverId, teamId });
        return SharedResult.err({ code: 'ALREADY_MEMBER', details: { message: 'Already a member or have a pending request' } });
      }

      const team = await this.teamRepository.findById(teamId);
      if (!team) {
        this.logger.error('Team not found', { entity: 'team', id: teamId });
        return SharedResult.err({ code: 'TEAM_NOT_FOUND', details: { message: `Team ${teamId} not found` } });
      }

      const membership: TeamMembership = {
        teamId,
        driverId,
        role: 'driver' as TeamRole,
        status: 'active' as TeamMembershipStatus,
        joinedAt: new Date(),
      };

      await this.membershipRepository.saveMembership(membership);
      this.logger.info('Driver successfully joined team', { driverId, teamId });
      return SharedResult.ok(undefined);
    } catch (error) {
      this.logger.error('Failed to join team due to an unexpected error', error instanceof Error ? error : new Error('Unknown error'));
      return SharedResult.err({ code: 'REPOSITORY_ERROR', details: { message: error instanceof Error ? error.message : 'Unknown error' } });
    }
  }
}