import { Injectable, Inject } from '@nestjs/common'; import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO'; import { UpdateTeamInputDTO } from './dtos/UpdateTeamInputDTO'; import { GetAllTeamsOutputDTO } from './dtos/GetAllTeamsOutputDTO'; import { GetTeamDetailsOutputDTO } from './dtos/GetTeamDetailsOutputDTO'; import { GetTeamMembersOutputDTO } from './dtos/GetTeamMembersOutputDTO'; import { GetTeamJoinRequestsOutputDTO } from './dtos/GetTeamJoinRequestsOutputDTO'; import { CreateTeamOutputDTO } from './dtos/CreateTeamOutputDTO'; import { UpdateTeamOutputDTO } from './dtos/UpdateTeamOutputDTO'; import { GetDriverTeamOutputDTO } from './dtos/GetDriverTeamOutputDTO'; import { GetTeamMembershipOutputDTO } from './dtos/GetTeamMembershipOutputDTO'; // Core imports import type { Logger } from '@core/shared/application/Logger'; import type { ITeamRepository } from '@core/racing/domain/repositories/ITeamRepository'; import type { ITeamMembershipRepository } from '@core/racing/domain/repositories/ITeamMembershipRepository'; import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository'; // Use cases import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase'; 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, CreateTeamInput } from '@core/racing/application/use-cases/CreateTeamUseCase'; import { UpdateTeamUseCase, UpdateTeamInput } from '@core/racing/application/use-cases/UpdateTeamUseCase'; import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase'; import { GetTeamMembershipUseCase } from '@core/racing/application/use-cases/GetTeamMembershipUseCase'; // Tokens import { TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN, TEAM_STATS_REPOSITORY_TOKEN } from './TeamTokens'; import type { ITeamStatsRepository } from '@core/racing/domain/repositories/ITeamStatsRepository'; @Injectable() export class TeamService { constructor( @Inject(TEAM_REPOSITORY_TOKEN) private readonly teamRepository: ITeamRepository, @Inject(TEAM_MEMBERSHIP_REPOSITORY_TOKEN) private readonly membershipRepository: ITeamMembershipRepository, @Inject(DRIVER_REPOSITORY_TOKEN) private readonly driverRepository: IDriverRepository, @Inject(LOGGER_TOKEN) private readonly logger: Logger, @Inject(TEAM_STATS_REPOSITORY_TOKEN) private readonly teamStatsRepository: ITeamStatsRepository, ) {} async getAll(): Promise { this.logger.debug('[TeamService] Fetching all teams.'); const useCase = new GetAllTeamsUseCase( this.teamRepository, this.membershipRepository, this.teamStatsRepository, this.logger ); const result = await useCase.execute({}); if (result.isErr()) { this.logger.error('Error fetching all teams', new Error(result.error?.details?.message || 'Unknown error')); return { teams: [], totalCount: 0 }; } const value = result.value; if (!value) { return { teams: [], totalCount: 0 }; } return { teams: value.teams.map(t => ({ id: t.team.id, name: t.team.name.toString(), tag: t.team.tag.toString(), description: t.description, memberCount: t.memberCount, leagues: t.leagues, totalWins: t.totalWins, totalRaces: t.totalRaces, performanceLevel: t.performanceLevel, specialization: t.specialization, region: t.region, languages: t.languages, rating: t.rating, logoUrl: t.logoUrl, isRecruiting: t.isRecruiting, })), totalCount: value.totalCount, }; } async getDetails(teamId: string, userId?: string): Promise { this.logger.debug(`[TeamService] Fetching team details for teamId: ${teamId}, userId: ${userId}`); const useCase = new GetTeamDetailsUseCase(this.teamRepository, this.membershipRepository); const result = await useCase.execute({ teamId, driverId: userId || '' }); if (result.isErr()) { this.logger.error(`Error fetching team details for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`); return null; } const value = result.value; if (!value) { return null; } // Convert to DTO return { team: { id: value.team.id, name: value.team.name.toString(), tag: value.team.tag.toString(), description: value.team.description.toString(), ownerId: value.team.ownerId.toString(), leagues: value.team.leagues.map(l => l.toString()), isRecruiting: value.team.isRecruiting, createdAt: value.team.createdAt?.toDate()?.toISOString?.() || new Date().toISOString(), category: undefined, }, membership: value.membership ? { role: value.membership.role === 'driver' ? 'member' : (value.membership.role as 'owner' | 'manager' | 'member'), joinedAt: value.membership.joinedAt.toISOString(), isActive: value.membership.status === 'active', } : null, canManage: value.canManage, }; } async getMembers(teamId: string): Promise { this.logger.debug(`[TeamService] Fetching team members for teamId: ${teamId}`); const useCase = new GetTeamMembersUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, this.logger); const result = await useCase.execute({ teamId }); if (result.isErr()) { this.logger.error(`Error fetching team members for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`); return { members: [], totalCount: 0, ownerCount: 0, managerCount: 0, memberCount: 0, }; } const value = result.value; if (!value) { return { members: [], totalCount: 0, ownerCount: 0, managerCount: 0, memberCount: 0, }; } return { members: value.members.map(m => ({ driverId: m.driver?.id || '', driverName: m.driver?.name?.toString() || '', role: m.membership.role === 'driver' ? 'member' : (m.membership.role as 'owner' | 'manager' | 'member'), joinedAt: m.membership.joinedAt.toISOString(), isActive: m.membership.status === 'active', avatarUrl: '', // Would need MediaResolver here })), totalCount: value.members.length, ownerCount: value.members.filter(m => m.membership.role === 'owner').length, managerCount: value.members.filter(m => m.membership.role === 'manager').length, memberCount: value.members.filter(m => m.membership.role === 'driver').length, }; } async getJoinRequests(teamId: string): Promise { this.logger.debug(`[TeamService] Fetching team join requests for teamId: ${teamId}`); const useCase = new GetTeamJoinRequestsUseCase(this.membershipRepository, this.driverRepository, this.teamRepository); const result = await useCase.execute({ teamId }); if (result.isErr()) { this.logger.error(`Error fetching team join requests for teamId: ${teamId}`, new Error(result.error?.details?.message || 'Unknown error')); return { requests: [], pendingCount: 0, totalCount: 0, }; } const value = result.value; if (!value) { return { requests: [], pendingCount: 0, totalCount: 0, }; } return { requests: value.joinRequests.map(r => ({ requestId: r.id, driverId: r.driverId, driverName: r.driver.name.toString(), teamId: r.teamId, status: 'pending', requestedAt: r.requestedAt.toISOString(), avatarUrl: '', // Would need MediaResolver here })), pendingCount: value.joinRequests.length, totalCount: value.joinRequests.length, }; } async create(input: CreateTeamInputDTO, userId?: string): Promise { this.logger.debug('[TeamService] Creating team', { input, userId }); const command: CreateTeamInput = { name: input.name, tag: input.tag, description: input.description ?? '', ownerId: userId || '', leagues: [], }; const useCase = new CreateTeamUseCase(this.teamRepository, this.membershipRepository, this.logger); const result = await useCase.execute(command); if (result.isErr()) { this.logger.error(`Error creating team: ${result.error?.details?.message || 'Unknown error'}`); return { id: '', success: false }; } const value = result.value; if (!value) { return { id: '', success: false }; } return { id: value.team.id, success: true }; } async update(teamId: string, input: UpdateTeamInputDTO, userId?: string): Promise { this.logger.debug(`[TeamService] Updating team ${teamId}`, { input, userId }); const command: UpdateTeamInput = { teamId, updates: { ...(input.name !== undefined && { name: input.name }), ...(input.tag !== undefined && { tag: input.tag }), ...(input.description !== undefined && { description: input.description }), }, updatedBy: userId || '', }; const useCase = new UpdateTeamUseCase(this.teamRepository, this.membershipRepository); const result = await useCase.execute(command); if (result.isErr()) { this.logger.error(`Error updating team ${teamId}: ${result.error?.details?.message || 'Unknown error'}`); return { success: false }; } return { success: true }; } async getDriverTeam(driverId: string): Promise { this.logger.debug(`[TeamService] Fetching team for driverId: ${driverId}`); const useCase = new GetDriverTeamUseCase(this.teamRepository, this.membershipRepository, this.logger); const result = await useCase.execute({ driverId }); if (result.isErr()) { this.logger.error(`Error fetching team for driverId: ${driverId}: ${result.error?.details?.message || 'Unknown error'}`); return null; } const value = result.value; if (!value || !value.team) { return null; } return { team: { id: value.team.id, name: value.team.name.toString(), tag: value.team.tag.toString(), description: value.team.description.toString(), ownerId: value.team.ownerId.toString(), leagues: value.team.leagues.map(l => l.toString()), isRecruiting: value.team.isRecruiting, createdAt: value.team.createdAt?.toDate?.()?.toISOString?.() || new Date().toISOString(), category: undefined, }, membership: { role: value.membership.role === 'driver' ? 'member' : (value.membership.role as 'owner' | 'manager' | 'member'), joinedAt: value.membership.joinedAt.toISOString(), isActive: value.membership.status === 'active', }, isOwner: value.membership.role === 'owner', canManage: value.membership.role === 'owner' || value.membership.role === 'manager', }; } async getMembership(teamId: string, driverId: string): Promise { this.logger.debug(`[TeamService] Fetching team membership for teamId: ${teamId}, driverId: ${driverId}`); const useCase = new GetTeamMembershipUseCase(this.membershipRepository, this.logger); const result = await useCase.execute({ teamId, driverId }); if (result.isErr()) { this.logger.error(`Error fetching team membership for teamId: ${teamId}, driverId: ${driverId}: ${result.error?.details?.message || 'Unknown error'}`); return null; } const value = result.value; if (!value) { return null; } return value.membership ? { role: value.membership.role, joinedAt: value.membership.joinedAt, isActive: value.membership.isActive, } : null; } }