/**
 * Get Dashboard Use Case
 *
 * Orchestrates the retrieval of dashboard data for a driver.
 * Aggregates data from multiple repositories and returns a unified dashboard view.
 */

import { DashboardRepository } from '../ports/DashboardRepository';
import { DashboardQuery } from '../ports/DashboardQuery';
import { DashboardDTO } from '../dto/DashboardDTO';
import { DashboardEventPublisher } from '../ports/DashboardEventPublisher';
import { DriverNotFoundError } from '../../domain/errors/DriverNotFoundError';
import { ValidationError } from '../../../shared/errors/ValidationError';

export interface GetDashboardUseCasePorts {
  driverRepository: DashboardRepository;
  raceRepository: DashboardRepository;
  leagueRepository: DashboardRepository;
  activityRepository: DashboardRepository;
  eventPublisher: DashboardEventPublisher;
}

export class GetDashboardUseCase {
  constructor(private readonly ports: GetDashboardUseCasePorts) {}

  async execute(query: DashboardQuery): Promise<DashboardDTO> {
    // Validate input
    this.validateQuery(query);

    // Find driver
    const driver = await this.ports.driverRepository.findDriverById(query.driverId);
    if (!driver) {
      throw new DriverNotFoundError(query.driverId);
    }

    // Fetch all data in parallel
    const [upcomingRaces, leagueStandings, recentActivity] = await Promise.all([
      this.ports.raceRepository.getUpcomingRaces(query.driverId),
      this.ports.leagueRepository.getLeagueStandings(query.driverId),
      this.ports.activityRepository.getRecentActivity(query.driverId),
    ]);

    // Limit upcoming races to 3
    const limitedRaces = upcomingRaces
      .sort((a, b) => a.scheduledDate.getTime() - b.scheduledDate.getTime())
      .slice(0, 3);

    // Sort recent activity by timestamp (newest first)
    const sortedActivity = recentActivity
      .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());

    // Transform to DTO
    const driverDto: DashboardDTO['driver'] = {
      id: driver.id,
      name: driver.name,
    };
    if (driver.avatar) {
      driverDto.avatar = driver.avatar;
    }

    const result: DashboardDTO = {
      driver: driverDto,
      statistics: {
        rating: driver.rating,
        rank: driver.rank,
        starts: driver.starts,
        wins: driver.wins,
        podiums: driver.podiums,
        leagues: driver.leagues,
      },
      upcomingRaces: limitedRaces.map(race => ({
        trackName: race.trackName,
        carType: race.carType,
        scheduledDate: race.scheduledDate.toISOString(),
        timeUntilRace: race.timeUntilRace || this.calculateTimeUntilRace(race.scheduledDate),
      })),
      championshipStandings: leagueStandings.map(standing => ({
        leagueName: standing.leagueName,
        position: standing.position,
        points: standing.points,
        totalDrivers: standing.totalDrivers,
      })),
      recentActivity: sortedActivity.map(activity => ({
        type: activity.type,
        description: activity.description,
        timestamp: activity.timestamp.toISOString(),
        status: activity.status,
      })),
    };

    // Publish event
    await this.ports.eventPublisher.publishDashboardAccessed({
      type: 'dashboard_accessed',
      driverId: query.driverId,
      timestamp: new Date(),
    });

    return result;
  }

  private validateQuery(query: DashboardQuery): void {
    if (!query.driverId || typeof query.driverId !== 'string') {
      throw new ValidationError('Driver ID must be a valid string');
    }
    if (query.driverId.trim().length === 0) {
      throw new ValidationError('Driver ID cannot be empty');
    }
  }

  private calculateTimeUntilRace(scheduledDate: Date): string {
    const now = new Date();
    const diff = scheduledDate.getTime() - now.getTime();
    
    if (diff <= 0) {
      return 'Race started';
    }

    const days = Math.floor(diff / (1000 * 60 * 60 * 24));
    const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));

    if (days > 0) {
      return `${days} day${days > 1 ? 's' : ''} ${hours} hour${hours > 1 ? 's' : ''}`;
    }
    if (hours > 0) {
      return `${hours} hour${hours > 1 ? 's' : ''} ${minutes} minute${minutes > 1 ? 's' : ''}`;
    }
    return `${minutes} minute${minutes > 1 ? 's' : ''}`;
  }
}
