import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { IImageServicePort } from '../ports/IImageServicePort';
import type { IFeedRepository } from '@core/social/domain/repositories/IFeedRepository';
import type { ISocialGraphRepository } from '@core/social/domain/repositories/ISocialGraphRepository';
import { Result } from '@core/shared/application/Result';
import { League } from '../../domain/entities/League';
import { Race } from '../../domain/entities/Race';
import { Result as RaceResult } from '../../domain/entities/Result';
import { Driver } from '../../domain/entities/Driver';
import { Standing } from '../../domain/entities/Standing';
import type { FeedItem } from '@core/social/domain/types/FeedItem';
import type {
  DashboardOverviewViewModel,
  DashboardDriverSummaryViewModel,
  DashboardRaceSummaryViewModel,
  DashboardRecentResultViewModel,
  DashboardLeagueStandingSummaryViewModel,
  DashboardFeedItemSummaryViewModel,
  DashboardFeedSummaryViewModel,
  DashboardFriendSummaryViewModel,
} from '../presenters/IDashboardOverviewPresenter';

interface DashboardOverviewParams {
  driverId: string;
}

interface DashboardDriverStatsAdapter {
  rating: number | null;
  wins: number;
  podiums: number;
  totalRaces: number;
  overallRank: number | null;
  consistency: number | null;
}

export class DashboardOverviewUseCase {
  constructor(
    private readonly driverRepository: IDriverRepository,
    private readonly raceRepository: IRaceRepository,
    private readonly resultRepository: IResultRepository,
    private readonly leagueRepository: ILeagueRepository,
    private readonly standingRepository: IStandingRepository,
    private readonly leagueMembershipRepository: ILeagueMembershipRepository,
    private readonly raceRegistrationRepository: IRaceRegistrationRepository,
    private readonly feedRepository: IFeedRepository,
    private readonly socialRepository: ISocialGraphRepository,
    private readonly imageService: IImageServicePort,
    private readonly getDriverStats: (driverId: string) => DashboardDriverStatsAdapter | null,
  ) {}

  async execute(params: DashboardOverviewParams): Promise<Result<DashboardOverviewViewModel>> {
    const { driverId } = params;

    const [driver, allLeagues, allRaces, allResults, feedItems, friends] = await Promise.all([
      this.driverRepository.findById(driverId),
      this.leagueRepository.findAll(),
      this.raceRepository.findAll(),
      this.resultRepository.findAll(),
      this.feedRepository.getFeedForDriver(driverId),
      this.socialRepository.getFriends(driverId),
    ]);

    const leagueMap = new Map(allLeagues.map(league => [league.id, league.name]));

    const driverStats = this.getDriverStats(driverId);

    const currentDriver: DashboardDriverSummaryViewModel | null = driver
      ? {
          id: driver.id,
          name: driver.name,
          country: driver.country,
          avatarUrl: this.imageService.getDriverAvatar(driver.id),
          rating: driverStats?.rating ?? null,
          globalRank: driverStats?.overallRank ?? null,
          totalRaces: driverStats?.totalRaces ?? 0,
          wins: driverStats?.wins ?? 0,
          podiums: driverStats?.podiums ?? 0,
          consistency: driverStats?.consistency ?? null,
        }
      : null;

    const driverLeagues = await this.getDriverLeagues(allLeagues, driverId);
    const driverLeagueIds = new Set(driverLeagues.map(league => league.id));

    const now = new Date();
    const upcomingRaces = allRaces
      .filter(race => race.status === 'scheduled' && race.scheduledAt > now)
      .sort((a, b) => a.scheduledAt.getTime() - b.scheduledAt.getTime());

    const upcomingRacesInDriverLeagues = upcomingRaces.filter(race =>
      driverLeagueIds.has(race.leagueId),
    );

    const { myUpcomingRaces, otherUpcomingRaces } =
      await this.partitionUpcomingRacesByRegistration(upcomingRacesInDriverLeagues, driverId, leagueMap);

    const nextRace: DashboardRaceSummaryViewModel | null =
      myUpcomingRaces.length > 0 ? myUpcomingRaces[0]! : null;

    const upcomingRacesSummaries: DashboardRaceSummaryViewModel[] = [
      ...myUpcomingRaces,
      ...otherUpcomingRaces,
    ].slice().sort(
      (a, b) =>
        new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime(),
    );

    const recentResults = this.buildRecentResults(allResults, allRaces, allLeagues, driverId);

    const leagueStandingsSummaries = await this.buildLeagueStandingsSummaries(
      driverLeagues,
      driverId,
    );

    const activeLeaguesCount = this.computeActiveLeaguesCount(
      upcomingRacesSummaries,
      leagueStandingsSummaries,
    );

    const feedSummary = this.buildFeedSummary(feedItems);

    const friendsSummary = this.buildFriendsSummary(friends);

    const viewModel: DashboardOverviewViewModel = {
      currentDriver,
      myUpcomingRaces,
      otherUpcomingRaces,
      upcomingRaces: upcomingRacesSummaries,
      activeLeaguesCount,
      nextRace,
      recentResults,
      leagueStandingsSummaries,
      feedSummary,
      friends: friendsSummary,
    };

    return Result.ok(viewModel);
  }

  private async getDriverLeagues(allLeagues: League[], driverId: string): Promise<League[]> {
    const driverLeagues: League[] = [];

    for (const league of allLeagues) {
      const membership = await this.leagueMembershipRepository.getMembership(league.id, driverId);
      if (membership && membership.status === 'active') {
        driverLeagues.push(league);
      }
    }

    return driverLeagues;
  }

  private async partitionUpcomingRacesByRegistration(
    upcomingRaces: Race[],
    driverId: string,
    leagueMap: Map<string, string>,
  ): Promise<{
    myUpcomingRaces: DashboardRaceSummaryViewModel[];
    otherUpcomingRaces: DashboardRaceSummaryViewModel[];
  }> {
    const myUpcomingRaces: DashboardRaceSummaryViewModel[] = [];
    const otherUpcomingRaces: DashboardRaceSummaryViewModel[] = [];

    for (const race of upcomingRaces) {
      const isRegistered = await this.raceRegistrationRepository.isRegistered(race.id, driverId);
      const summary = this.mapRaceToSummary(race, leagueMap, true);

      if (isRegistered) {
        myUpcomingRaces.push(summary);
      } else {
        otherUpcomingRaces.push(summary);
      }
    }

    return { myUpcomingRaces, otherUpcomingRaces };
  }

  private mapRaceToSummary(
    race: Race,
    leagueMap: Map<string, string>,
    isMyLeague: boolean,
  ): DashboardRaceSummaryViewModel {
    return {
      id: race.id,
      leagueId: race.leagueId,
      leagueName: leagueMap.get(race.leagueId) ?? 'Unknown League',
      track: race.track,
      car: race.car,
      scheduledAt: race.scheduledAt.toISOString(),
      status: race.status,
      isMyLeague,
    };
  }

  private buildRecentResults(
    allResults: RaceResult[],
    allRaces: Race[],
    allLeagues: League[],
    driverId: string,
  ): DashboardRecentResultViewModel[] {
    const raceById = new Map(allRaces.map(race => [race.id, race]));
    const leagueById = new Map(allLeagues.map(league => [league.id, league]));

    const driverResults = allResults.filter(result => result.driverId === driverId);

    const enriched = driverResults
      .map(result => {
        const race = raceById.get(result.raceId);
        if (!race) return null;

        const league = leagueById.get(race.leagueId);

        const finishedAt = race.scheduledAt.toISOString();

        const item: DashboardRecentResultViewModel = {
          raceId: race.id,
          raceName: race.track,
          leagueId: race.leagueId,
          leagueName: league?.name ?? 'Unknown League',
          finishedAt,
          position: result.position,
          incidents: result.incidents,
        };

        return item;
      })
      .filter((item): item is DashboardRecentResultViewModel => !!item)
      .sort(
        (a, b) =>
          new Date(b.finishedAt).getTime() - new Date(a.finishedAt).getTime(),
      );

    const RECENT_RESULTS_LIMIT = 5;

    return enriched.slice(0, RECENT_RESULTS_LIMIT);
  }

  private async buildLeagueStandingsSummaries(
    driverLeagues: League[],
    driverId: string,
  ): Promise<DashboardLeagueStandingSummaryViewModel[]> {
    const summaries: DashboardLeagueStandingSummaryViewModel[] = [];

    for (const league of driverLeagues.slice(0, 3)) {
      const standings = await this.standingRepository.findByLeagueId(league.id);
      const driverStanding = standings.find(
        (standing: Standing) => standing.driverId === driverId,
      );

      summaries.push({
        leagueId: league.id,
        leagueName: league.name,
        position: driverStanding?.position ?? 0,
        points: driverStanding?.points ?? 0,
        totalDrivers: standings.length,
      });
    }

    return summaries;
  }

  private computeActiveLeaguesCount(
    upcomingRaces: DashboardRaceSummaryViewModel[],
    leagueStandingsSummaries: DashboardLeagueStandingSummaryViewModel[],
  ): number {
    const activeLeagueIds = new Set<string>();

    for (const race of upcomingRaces) {
      activeLeagueIds.add(race.leagueId);
    }

    for (const standing of leagueStandingsSummaries) {
      activeLeagueIds.add(standing.leagueId);
    }

    return activeLeagueIds.size;
  }

  private buildFeedSummary(feedItems: FeedItem[]): DashboardFeedSummaryViewModel {
    const items: DashboardFeedItemSummaryViewModel[] = feedItems.map(item => ({
      id: item.id,
      type: item.type,
      headline: item.headline,
      timestamp:
        item.timestamp instanceof Date
          ? item.timestamp.toISOString()
          : new Date(item.timestamp).toISOString(),
      ...(item.body !== undefined ? { body: item.body } : {}),
      ...(item.ctaLabel !== undefined ? { ctaLabel: item.ctaLabel } : {}),
      ...(item.ctaHref !== undefined ? { ctaHref: item.ctaHref } : {}),
    }));

    return {
      notificationCount: items.length,
      items,
    };
  }

  private buildFriendsSummary(friends: Driver[]): DashboardFriendSummaryViewModel[] {
    return friends.map(friend => ({
      id: friend.id,
      name: friend.name,
      country: friend.country,
      avatarUrl: this.imageService.getDriverAvatar(friend.id),
    }));
  }
}