import { Injectable, Inject } from '@nestjs/common';
import { AllTeamsViewModel, GetDriverTeamQuery, DriverTeamViewModel, TeamDetailsViewModel, TeamMembersViewModel, TeamJoinRequestsViewModel, CreateTeamInput, CreateTeamOutput, UpdateTeamInput, UpdateTeamOutput, ApproveTeamJoinRequestInput, ApproveTeamJoinRequestOutput, RejectTeamJoinRequestInput, RejectTeamJoinRequestOutput } from './dto/TeamDto';

// Use cases
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
import { GetTeamDetailsUseCase } from '@core/racing/application/use-cases/GetTeamDetailsUseCase';
import { GetTeamMembersUseCase } from '@core/racing/application/use-cases/GetTeamMembersUseCase';
import { GetTeamJoinRequestsUseCase } from '@core/racing/application/use-cases/GetTeamJoinRequestsUseCase';
import { CreateTeamUseCase } from '@core/racing/application/use-cases/CreateTeamUseCase';
import { UpdateTeamUseCase } from '@core/racing/application/use-cases/UpdateTeamUseCase';
import { ApproveTeamJoinRequestUseCase } from '@core/racing/application/use-cases/ApproveTeamJoinRequestUseCase';
import { RejectTeamJoinRequestUseCase } from '@core/racing/application/use-cases/RejectTeamJoinRequestUseCase';

// Presenters
import { AllTeamsPresenter } from './presenters/AllTeamsPresenter';
import { DriverTeamPresenter } from './presenters/DriverTeamPresenter';
import { TeamDetailsPresenter } from './presenters/TeamDetailsPresenter';
import { TeamMembersPresenter } from './presenters/TeamMembersPresenter';
import { TeamJoinRequestsPresenter } from './presenters/TeamJoinRequestsPresenter';

// Logger
import type { Logger } from '@core/shared/application/Logger';

// Tokens
import {
  TEAM_GET_ALL_USE_CASE_TOKEN,
  TEAM_GET_DRIVER_TEAM_USE_CASE_TOKEN,
  TEAM_GET_DETAILS_USE_CASE_TOKEN,
  TEAM_GET_MEMBERS_USE_CASE_TOKEN,
  TEAM_GET_JOIN_REQUESTS_USE_CASE_TOKEN,
  TEAM_CREATE_USE_CASE_TOKEN,
  TEAM_UPDATE_USE_CASE_TOKEN,
  TEAM_APPROVE_JOIN_REQUEST_USE_CASE_TOKEN,
  TEAM_REJECT_JOIN_REQUEST_USE_CASE_TOKEN,
  TEAM_GET_LEADERBOARD_USE_CASE_TOKEN,
  TEAM_LOGGER_TOKEN
} from './TeamProviders';

@Injectable()
export class TeamService {
  constructor(
    @Inject(TEAM_GET_ALL_USE_CASE_TOKEN) private readonly getAllTeamsUseCase: GetAllTeamsUseCase,
    @Inject(TEAM_GET_DRIVER_TEAM_USE_CASE_TOKEN) private readonly getDriverTeamUseCase: GetDriverTeamUseCase,
    @Inject(TEAM_GET_DETAILS_USE_CASE_TOKEN) private readonly getTeamDetailsUseCase: GetTeamDetailsUseCase,
    @Inject(TEAM_GET_MEMBERS_USE_CASE_TOKEN) private readonly getTeamMembersUseCase: GetTeamMembersUseCase,
    @Inject(TEAM_GET_JOIN_REQUESTS_USE_CASE_TOKEN) private readonly getTeamJoinRequestsUseCase: GetTeamJoinRequestsUseCase,
    @Inject(TEAM_CREATE_USE_CASE_TOKEN) private readonly createTeamUseCase: CreateTeamUseCase,
    @Inject(TEAM_UPDATE_USE_CASE_TOKEN) private readonly updateTeamUseCase: UpdateTeamUseCase,
    @Inject(TEAM_APPROVE_JOIN_REQUEST_USE_CASE_TOKEN) private readonly approveTeamJoinRequestUseCase: ApproveTeamJoinRequestUseCase,
    @Inject(TEAM_REJECT_JOIN_REQUEST_USE_CASE_TOKEN) private readonly rejectTeamJoinRequestUseCase: RejectTeamJoinRequestUseCase,
    @Inject(TEAM_GET_LEADERBOARD_USE_CASE_TOKEN) private readonly getTeamsLeaderboardUseCase: GetTeamsLeaderboardUseCase,
    @Inject(TEAM_LOGGER_TOKEN) private readonly logger: Logger,
  ) {}

  async getAllTeams(): Promise<AllTeamsViewModel> {
    this.logger.debug('[TeamService] Fetching all teams.');

    const presenter = new AllTeamsPresenter();
    await this.getAllTeamsUseCase.execute(undefined, presenter);
    return presenter.viewModel as unknown as AllTeamsViewModel;
  }

  async getDriverTeam(query: GetDriverTeamQuery): Promise<DriverTeamViewModel | null> {
    this.logger.debug(`[TeamService] Fetching driver team for driverId: ${query.driverId}`);

    const presenter = new DriverTeamPresenter();
    try {
      await this.getDriverTeamUseCase.execute({ driverId: query.driverId }, presenter);
      return presenter.viewModel as unknown as DriverTeamViewModel;
    } catch (error) {
      this.logger.error(`Error fetching driver team: ${error}`);
      return null;
    }
  }

  async getTeamDetails(teamId: string): Promise<TeamDetailsViewModel | null> {
    this.logger.debug(`[TeamService] Fetching team details for teamId: ${teamId}`);

    const presenter = new TeamDetailsPresenter();
    try {
      await this.getTeamDetailsUseCase.execute({ teamId, driverId: '' }, presenter);
      return presenter.viewModel as unknown as TeamDetailsViewModel;
    } catch (error) {
      this.logger.error(`Error fetching team details: ${error}`);
      return null;
    }
  }

  async getTeamMembers(teamId: string): Promise<TeamMembersViewModel> {
    this.logger.debug(`[TeamService] Fetching team members for teamId: ${teamId}`);

    const presenter = new TeamMembersPresenter();
    await this.getTeamMembersUseCase.execute({ teamId }, presenter);
    return presenter.viewModel as unknown as TeamMembersViewModel;
  }

  async getTeamJoinRequests(teamId: string): Promise<TeamJoinRequestsViewModel> {
    this.logger.debug(`[TeamService] Fetching join requests for teamId: ${teamId}`);

    const presenter = new TeamJoinRequestsPresenter();
    await this.getTeamJoinRequestsUseCase.execute({ teamId }, presenter);
    return presenter.viewModel as unknown as TeamJoinRequestsViewModel;
  }

  async createTeam(input: CreateTeamInput): Promise<CreateTeamOutput> {
    this.logger.debug('[TeamService] Creating team', input);

    try {
      const result = await this.createTeamUseCase.execute({
        name: input.name,
        tag: input.tag,
        description: input.description,
        ownerId: input.ownerId,
        leagues: [],
      });
      return {
        teamId: result.team.id,
        success: true,
      };
    } catch (error) {
      this.logger.error(`Error creating team: ${error}`);
      throw error;
    }
  }

  async updateTeam(input: UpdateTeamInput & { teamId: string }): Promise<UpdateTeamOutput> {
    this.logger.debug('[TeamService] Updating team', input);

    try {
      await this.updateTeamUseCase.execute({
        teamId: input.teamId,
        updates: {
          name: input.name,
          tag: input.tag,
          description: input.description,
        },
        updatedBy: input.updatedBy,
      });
      return { success: true };
    } catch (error) {
      this.logger.error(`Error updating team: ${error}`);
      throw error;
    }
  }

  async approveTeamJoinRequest(input: ApproveTeamJoinRequestInput & { teamId: string }): Promise<ApproveTeamJoinRequestOutput> {
    this.logger.debug('[TeamService] Approving team join request', input);

    try {
      await this.approveTeamJoinRequestUseCase.execute({ requestId: input.requestId });
      return { success: true };
    } catch (error) {
      this.logger.error(`Error approving join request: ${error}`);
      throw error;
    }
  }

  async rejectTeamJoinRequest(input: RejectTeamJoinRequestInput & { teamId: string }): Promise<RejectTeamJoinRequestOutput> {
    this.logger.debug('[TeamService] Rejecting team join request', input);

    try {
      await this.rejectTeamJoinRequestUseCase.execute({ requestId: input.requestId });
      return { success: true };
    } catch (error) {
      this.logger.error(`Error rejecting join request: ${error}`);
      throw error;
    }
  }

  async getTeamsLeaderboard(): Promise<any> {
    this.logger.debug('[TeamService] Fetching teams leaderboard');

    const result = await this.getTeamsLeaderboardUseCase.execute();
    if (result.isErr()) {
      this.logger.error(`Error fetching teams leaderboard: ${result.error}`);
      throw new Error('Failed to fetch teams leaderboard');
    }
    return result.value;
  }
}
