import { Driver } from '@core/racing/domain/entities/Driver';
import { League } from '@core/racing/domain/entities/League';
import { Race } from '@core/racing/domain/entities/Race';
import type { Result } from '@core/racing/domain/entities/Result';
import type { FeedItem } from '@core/social/domain/types/FeedItem';
import type { FriendDTO } from '@core/social/application/dto/FriendDTO';
import { faker } from '../../helpers/faker/faker';
import { getLeagueBanner, getDriverAvatar } from '../../helpers/images/images';
import type { Friendship, RacingMembership } from './RacingSeedCore';

/**
 * Feed events and derived racing demo data.
 * Extracted from the legacy StaticRacingSeed module to keep files smaller.
 */
export function createFeedEvents(
  drivers: Driver[],
  leagues: League[],
  races: Race[],
  friendships: Friendship[],
): FeedItem[] {
  const events: FeedItem[] = [];
  const now = new Date();
  const completedRaces = races.filter((race) => race.status === 'completed');

  // Focus the global feed around a stable “core” of demo drivers
  const coreDrivers = faker.helpers.shuffle(drivers).slice(0, Math.min(16, drivers.length));

  coreDrivers.forEach((driver, index) => {
    const league = pickOne(leagues);
    const raceSource = completedRaces.length > 0 ? completedRaces : races;
    const race = pickOne(raceSource);
    const minutesAgo = 10 + index * 5;
    const baseTimestamp = new Date(now.getTime() - minutesAgo * 60 * 1000);

    const actorFriendId = driver.id;

    // Joined league
    events.push({
      id: `friend-joined-league:${driver.id}:${minutesAgo}`,
      type: 'friend-joined-league',
      timestamp: baseTimestamp,
      actorDriverId: driver.id,
      actorFriendId,
      leagueId: league.id,
      headline: `${driver.name} joined ${league.name}`,
      body: 'They are now registered for the full season.',
      ctaLabel: 'View league',
      ctaHref: `/leagues/${league.id}`,
    });

    // Finished race / podium highlight
    const finishingPosition = (index % 5) + 1;
    events.push({
      id: `friend-finished-race:${driver.id}:${minutesAgo}`,
      type: 'friend-finished-race',
      timestamp: new Date(baseTimestamp.getTime() - 8 * 60 * 1000),
      actorDriverId: driver.id,
      actorFriendId,
      leagueId: race.leagueId,
      raceId: race.id,
      position: finishingPosition,
      headline: `${driver.name} finished P${finishingPosition} at ${race.track}`,
      body:
        finishingPosition <= 3
          ? `${driver.name} scored a podium in ${race.car}.`
          : `${driver.name} secured a strong result in ${race.car}.`,
      ctaLabel: 'View results',
      ctaHref: `/races/${race.id}/results`,
    });

    // New personal best
    events.push({
      id: `friend-new-personal-best:${driver.id}:${minutesAgo}`,
      type: 'friend-new-personal-best',
      timestamp: new Date(baseTimestamp.getTime() - 20 * 60 * 1000),
      actorDriverId: driver.id,
      actorFriendId,
      leagueId: race.leagueId,
      raceId: race.id,
      headline: `${driver.name} set a new personal best at ${race.track}`,
      body: 'Consistency and pace are trending up this season.',
      ctaLabel: 'View lap chart',
      ctaHref: `/races/${race.id}/analysis`,
    });

    // Joined team (where applicable)
    const driverFriendships = friendships.filter((f) => f.driverId === driver.id);
    if (driverFriendships.length > 0) {
      const friend = pickOne(driverFriendships);
      const teammate = drivers.find((d) => d.id === friend.friendId);
      if (teammate) {
        events.push({
          id: `friend-joined-team:${driver.id}:${minutesAgo}`,
          type: 'friend-joined-team',
          timestamp: new Date(baseTimestamp.getTime() - 30 * 60 * 1000),
          actorDriverId: driver.id,
          actorFriendId,
          headline: `${driver.name} and ${teammate.name} are now teammates`,
          body: 'They will be sharing strategy and setups this season.',
          ctaLabel: 'View team',
          ctaHref: '/teams',
        });
      }
    }

    // League highlight
    events.push({
      id: `league-highlight:${league.id}:${minutesAgo}`,
      type: 'league-highlight',
      timestamp: new Date(baseTimestamp.getTime() - 45 * 60 * 1000),
      leagueId: league.id,
      headline: `${league.name} active with ${drivers.length}+ drivers`,
      body: 'Participation is growing. Perfect time to join the grid.',
      ctaLabel: 'Explore league',
      ctaHref: `/leagues/${league.id}`,
    });
  });

  // Global “system” events: new race scheduled and results posted
  const upcomingRaces = races.filter((race) => race.status === 'scheduled').slice(0, 8);
  upcomingRaces.forEach((race, index) => {
    const minutesAgo = 60 + index * 15;
    const timestamp = new Date(now.getTime() - minutesAgo * 60 * 1000);
    events.push({
      id: `new-race-scheduled:${race.id}`,
      type: 'new-race-scheduled',
      timestamp,
      leagueId: race.leagueId,
      raceId: race.id,
      headline: `New race scheduled at ${race.track}`,
      body: `${race.car} • ${race.scheduledAt.toLocaleString()}`,
      ctaLabel: 'View schedule',
      ctaHref: `/races/${race.id}`,
    });
  });

  const completedForResults = completedRaces.slice(0, 8);
  completedForResults.forEach((race, index) => {
    const minutesAgo = 180 + index * 20;
    const timestamp = new Date(now.getTime() - minutesAgo * 60 * 1000);
    events.push({
      id: `new-result-posted:${race.id}`,
      type: 'new-result-posted',
      timestamp,
      leagueId: race.leagueId,
      raceId: race.id,
      headline: `Results posted for ${race.track}`,
      body: 'Standings and stats updated across the grid.',
      ctaLabel: 'View classification',
      ctaHref: `/races/${race.id}/results`,
    });
  });

  const sorted = events
    .slice()
    .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());

  return sorted;
}

/**
 * Derived friend DTOs for UI consumption.
 * This preserves the previous demo-data `friends` shape while
 * keeping generation logic separate from the main seed.
 */
export function buildFriends(
  drivers: Driver[],
  memberships: RacingMembership[],
): FriendDTO[] {
  return drivers.map((driver) => {
    const membership = memberships.find((m) => m.driverId === driver.id);

    const base: FriendDTO = {
      driverId: driver.id,
      displayName: driver.name,
      avatarUrl: getDriverAvatar(driver.id),
      isOnline: true,
      lastSeen: new Date(),
    };

    const withLeague =
      membership?.leagueId !== undefined
        ? { ...base, primaryLeagueId: membership.leagueId }
        : base;

    const withTeam =
      membership?.teamId !== undefined
        ? { ...withLeague, primaryTeamId: membership.teamId }
        : withLeague;

    return withTeam;
  });
}

/**
 * Build top leagues with banner URLs for UI.
 */
export type LeagueWithBannerDTO = {
  id: string;
  name: string;
  description: string;
  ownerId: string;
  settings: League['settings'];
  createdAt: Date;
  socialLinks: League['socialLinks'];
  bannerUrl: string;
};

export function buildTopLeagues(leagues: League[]): LeagueWithBannerDTO[] {
  return leagues.map((league) => ({
    id: league.id,
    name: league.name,
    description: league.description,
    ownerId: league.ownerId,
    settings: league.settings,
    createdAt: league.createdAt,
    socialLinks: league.socialLinks,
    bannerUrl: getLeagueBanner(league.id),
  }));
}

export type RaceWithResultsDTO = {
  raceId: string;
  track: string;
  car: string;
  scheduledAt: Date;
  winnerDriverId: string;
  winnerName: string;
};

/**
 * Utility to get upcoming races from a given race list.
 */
export function buildUpcomingRaces(
  races: Race[],
  limit?: number,
): readonly Race[] {
  const upcoming = races.filter((race) => race.status === 'scheduled');
  const sorted = upcoming
    .slice()
    .sort((a, b) => a.scheduledAt.getTime() - b.scheduledAt.getTime());
  return typeof limit === 'number' ? sorted.slice(0, limit) : sorted;
}

/**
 * Utility to get latest race results from races + results + drivers.
 */
export function buildLatestResults(
  races: Race[],
  results: Result[],
  drivers: Driver[],
  limit?: number,
): readonly RaceWithResultsDTO[] {
  const completedRaces = races.filter((race) => race.status === 'completed');

  const joined = completedRaces.map((race) => {
    const raceResults = results
      .filter((result) => result.raceId === race.id)
      .slice()
      .sort((a, b) => a.position - b.position);
    const winner = raceResults[0];
    const winnerDriver =
      winner && drivers.find((driver) => driver.id === winner.driverId);

    return {
      raceId: race.id,
      track: race.track,
      car: race.car,
      scheduledAt: race.scheduledAt,
      winnerDriverId: winner?.driverId ?? '',
      winnerName: winnerDriver?.name ?? 'Winner',
    };
  });

  const sorted = joined
    .slice()
    .sort((a, b) => b.scheduledAt.getTime() - a.scheduledAt.getTime());

  return typeof limit === 'number' ? sorted.slice(0, limit) : sorted;
}

/**
 * Local helper: random pick from an array.
 * Kept here to avoid importing from core in callers that only care about feed.
 */
function pickOne<T>(items: readonly T[]): T {
  if (items.length === 0) {
    throw new Error('pickOne: empty items array');
  }
  const index = faker.number.int({ min: 0, max: items.length - 1 });
  return items[index]!;
}