/**
 * Get Driver Rankings Use Case
 *
 * Orchestrates the retrieval of driver rankings data.
 * Aggregates data from repositories and returns drivers with search, filter, and sort capabilities.
 */

import { LeaderboardsRepository } from '../ports/LeaderboardsRepository';
import { LeaderboardsEventPublisher } from '../ports/LeaderboardsEventPublisher';
import {
  DriverRankingsQuery,
  DriverRankingsResult,
  DriverRankingEntry,
  PaginationMetadata,
} from '../ports/DriverRankingsQuery';
import { ValidationError } from '../../../shared/errors/ValidationError';

export interface GetDriverRankingsUseCasePorts {
  leaderboardsRepository: LeaderboardsRepository;
  eventPublisher: LeaderboardsEventPublisher;
}

export class GetDriverRankingsUseCase {
  constructor(private readonly ports: GetDriverRankingsUseCasePorts) {}

  async execute(query: DriverRankingsQuery = {}): Promise<DriverRankingsResult> {
    try {
      // Validate query parameters
      this.validateQuery(query);

      const page = query.page ?? 1;
      const limit = query.limit ?? 20;

      // Fetch all drivers
      const allDrivers = await this.ports.leaderboardsRepository.findAllDrivers();

      // Apply search filter
      let filteredDrivers = allDrivers;
      if (query.search) {
        const searchLower = query.search.toLowerCase();
        filteredDrivers = filteredDrivers.filter((driver) =>
          driver.name.toLowerCase().includes(searchLower),
        );
      }

      // Apply rating filter
      if (query.minRating !== undefined) {
        filteredDrivers = filteredDrivers.filter(
          (driver) => driver.rating >= query.minRating!,
        );
      }

      // Apply team filter
      if (query.teamId) {
        filteredDrivers = filteredDrivers.filter(
          (driver) => driver.teamId === query.teamId,
        );
      }

      // Sort drivers
      const sortBy = query.sortBy ?? 'rating';
      const sortOrder = query.sortOrder ?? 'desc';

      filteredDrivers.sort((a, b) => {
        let comparison = 0;

        switch (sortBy) {
          case 'rating':
            comparison = a.rating - b.rating;
            break;
          case 'name':
            comparison = a.name.localeCompare(b.name);
            break;
          case 'rank':
            comparison = 0;
            break;
          case 'raceCount':
            comparison = a.raceCount - b.raceCount;
            break;
        }

        // If primary sort is equal, always use name ASC as secondary sort
        if (comparison === 0 && sortBy !== 'name') {
          comparison = a.name.localeCompare(b.name);
          // Secondary sort should not be affected by sortOrder of primary field?
          // Actually, usually secondary sort is always ASC or follows primary.
          // Let's keep it simple: if primary is equal, use name ASC.
          return comparison;
        }

        return sortOrder === 'asc' ? comparison : -comparison;
      });

      // Calculate pagination
      const total = filteredDrivers.length;
      const totalPages = Math.ceil(total / limit);
      const startIndex = (page - 1) * limit;
      const endIndex = Math.min(startIndex + limit, total);

      // Get paginated drivers
      const paginatedDrivers = filteredDrivers.slice(startIndex, endIndex);

      // Map to ranking entries with rank
      const driverEntries: DriverRankingEntry[] = paginatedDrivers.map(
        (driver, index): DriverRankingEntry => ({
          rank: startIndex + index + 1,
          id: driver.id,
          name: driver.name,
          rating: driver.rating,
          ...(driver.teamId !== undefined && { teamId: driver.teamId }),
          ...(driver.teamName !== undefined && { teamName: driver.teamName }),
          raceCount: driver.raceCount,
        }),
      );

      // Publish event
      await this.ports.eventPublisher.publishDriverRankingsAccessed({
        type: 'driver_rankings_accessed',
        timestamp: new Date(),
      });

      return {
        drivers: driverEntries,
        pagination: {
          total,
          page,
          limit,
          totalPages,
        },
      };
    } catch (error) {
      // Publish error event
      await this.ports.eventPublisher.publishLeaderboardsError({
        type: 'leaderboards_error',
        error: error instanceof Error ? error.message : String(error),
        timestamp: new Date(),
      });
      throw error;
    }
  }

  private validateQuery(query: DriverRankingsQuery): void {
    if (query.page !== undefined && query.page < 1) {
      throw new ValidationError('Page must be a positive integer');
    }

    if (query.limit !== undefined && query.limit < 1) {
      throw new ValidationError('Limit must be a positive integer');
    }

    if (query.minRating !== undefined && query.minRating < 0) {
      throw new ValidationError('Min rating must be a non-negative number');
    }

    if (query.sortBy && !['rating', 'name', 'rank', 'raceCount'].includes(query.sortBy)) {
      throw new ValidationError('Invalid sort field');
    }

    if (query.sortOrder && !['asc', 'desc'].includes(query.sortOrder)) {
      throw new ValidationError('Sort order must be "asc" or "desc"');
    }
  }
}
