import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
import { LeaguesApiClient } from "@/lib/api/leagues/LeaguesApiClient";
import { RacesApiClient } from "@/lib/api/races/RacesApiClient";
import { ApiError } from '@/lib/api/base/ApiError';
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
import { isProductionEnvironment } from '@/lib/config/env';
import { Result } from '@/lib/contracts/Result';
import { DomainError, Service } from '@/lib/contracts/services/Service';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
import { AllLeaguesWithCapacityAndScoringDTO } from '@/lib/types/AllLeaguesWithCapacityAndScoringDTO';
import { CreateLeagueInputDTO } from "@/lib/types/generated/CreateLeagueInputDTO";
import type { CreateLeagueScheduleRaceInputDTO } from '@/lib/types/generated/CreateLeagueScheduleRaceInputDTO';
import type { CreateLeagueScheduleRaceOutputDTO } from '@/lib/types/generated/CreateLeagueScheduleRaceOutputDTO';
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
import type { LeagueRosterJoinRequestDTO } from "@/lib/types/generated/LeagueRosterJoinRequestDTO";
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
import type { LeagueScheduleDTO } from '@/lib/types/generated/LeagueScheduleDTO';
import type { LeagueScheduleRaceMutationSuccessDTO } from '@/lib/types/generated/LeagueScheduleRaceMutationSuccessDTO';
import type { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';
import type { LeagueSeasonSchedulePublishOutputDTO } from '@/lib/types/generated/LeagueSeasonSchedulePublishOutputDTO';
import type { LeagueSeasonSummaryDTO } from '@/lib/types/generated/LeagueSeasonSummaryDTO';
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
import type { RaceDTO } from '@/lib/types/generated/RaceDTO';
import type { UpdateLeagueScheduleRaceInputDTO } from '@/lib/types/generated/UpdateLeagueScheduleRaceInputDTO';
import type { MembershipRole } from "@/lib/types/MembershipRole";
import { injectable, unmanaged } from 'inversify';

// TODO these data interfaces violate our architecture, see VIEW_DATA
export interface LeagueScheduleAdminData {
  leagueId: string;
  seasonId: string;
  seasons: LeagueSeasonSummaryDTO[];
  schedule: LeagueScheduleDTO;
}

export interface LeagueRosterAdminData {
  leagueId: string;
  members: LeagueRosterMemberDTO[];
  joinRequests: LeagueRosterJoinRequestDTO[];
}

export interface LeagueDetailData {
  league: LeagueWithCapacityAndScoringDTO;
  owner: GetDriverOutputDTO | null;
  scoringConfig: LeagueScoringConfigDTO | null;
  memberships: LeagueMembershipsDTO;
  races: RaceDTO[];
  sponsors: any[];
}

/**
 * League Service - DTO Only
 *
 * Returns Result<ApiDto, DomainError>. No ViewModels or UX logic.
 * All client-side presentation logic must be handled by hooks/components.
 * @server-safe
 */
@injectable()
export class LeagueService implements Service {
  private readonly baseUrl: string;
  private apiClient: LeaguesApiClient;
  private driversApiClient: DriversApiClient;
  private racesApiClient: RacesApiClient;

  constructor(@unmanaged() apiClient?: LeaguesApiClient) {
    const baseUrl = getWebsiteApiBaseUrl();
    this.baseUrl = baseUrl;
    const logger = new ConsoleLogger();
    const errorReporter = new EnhancedErrorReporter(logger, {
      showUserNotifications: false,
      logToConsole: true,
      reportToExternal: isProductionEnvironment(),
    });

    if (apiClient) {
      this.apiClient = apiClient;
    } else {
      this.apiClient = new LeaguesApiClient(baseUrl, errorReporter, logger);
    }

    this.driversApiClient = new DriversApiClient(baseUrl, errorReporter, logger);
    this.racesApiClient = new RacesApiClient(baseUrl, errorReporter, logger);
  }

  async getLeagueStandings(leagueId: string): Promise<any> {
    try {
      const data = await this.apiClient.getStandings(leagueId);
      return (data as any).value || data;
    } catch (error: unknown) {
      throw error;
    }
  }

  async getLeagueStats(): Promise<any> {
    try {
      const data = await this.apiClient.getTotal();
      return (data as any).value || data;
    } catch (error: unknown) {
      throw error;
    }
  }

  async getLeagueSchedule(leagueId: string): Promise<any> {
    try {
      const data = await this.apiClient.getSchedule(leagueId);
      return (data as any).value || data;
    } catch (error: unknown) {
      throw error;
    }
  }

  async getLeagueMemberships(leagueId: string): Promise<any> {
    try {
      const data = await this.apiClient.getMemberships(leagueId);
      return (data as any).value || data;
    } catch (error: unknown) {
      throw error;
    }
  }

  async createLeague(input: CreateLeagueInputDTO): Promise<any> {
    try {
      const data = await this.apiClient.create(input);
      return (data as any).value || data;
    } catch (error: unknown) {
      throw error;
    }
  }

  async removeMember(leagueId: string, targetDriverId: string): Promise<any> {
    try {
      const dto = await this.apiClient.removeRosterMember(leagueId, targetDriverId);
      return { success: dto.success };
    } catch (error: unknown) {
      throw error;
    }
  }

  async getAllLeagues(): Promise<Result<AllLeaguesWithCapacityAndScoringDTO, DomainError>> {
    try {
      const dto = await this.apiClient.getAllWithCapacityAndScoring();
      return Result.ok(dto);
    } catch (error: unknown) {
      // Map API error types to domain error types
      if (error instanceof ApiError) {
        const errorType = error.type;
        switch (errorType) {
          case 'NOT_FOUND':
            return Result.err({ type: 'notFound', message: error.message });
          case 'AUTH_ERROR':
            return Result.err({ type: 'unauthorized', message: error.message });
          case 'SERVER_ERROR':
            return Result.err({ type: 'serverError', message: error.message });
          default:
            return Result.err({ type: 'serverError', message: error.message || 'Failed to fetch leagues' });
        }
      }
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch leagues' });
    }
  }

  async getLeagueDetailData(leagueId: string): Promise<Result<LeagueDetailData, DomainError>> {
    try {
      const [apiDto, memberships, racesPageData] = await Promise.all([
        this.apiClient.getAllWithCapacityAndScoring(),
        this.apiClient.getMemberships(leagueId),
        this.racesApiClient.getPageData(leagueId),
      ]);

      if (process.env.NODE_ENV !== 'production') {
        const membershipCount = Array.isArray(memberships?.members) ? memberships.members.length : 0;
        const racesCount = Array.isArray(racesPageData?.races) ? racesPageData.races.length : 0;
        const race0 = racesCount > 0 ? racesPageData.races[0] : null;

        console.info(
          '[LeagueService.getLeagueDetailData] baseUrl=%s leagueId=%s memberships=%d races=%d race0=%o',
          this.baseUrl,
          leagueId,
          membershipCount,
          racesCount,
          race0,
        );

      }
      
      if (!apiDto || !apiDto.leagues) {
        return Result.err({ type: 'notFound', message: 'Leagues not found' });
      }
      
      const league = apiDto.leagues.find(l => l.id === leagueId);
      if (!league) {
        return Result.err({ type: 'notFound', message: 'League not found' });
      }

      // Fetch owner if ownerId exists
      let owner: GetDriverOutputDTO | null = null;
      if (league.ownerId) {
        owner = await this.driversApiClient.getDriver(league.ownerId);
      }

      // Fetch scoring config if available
      let scoringConfig: LeagueScoringConfigDTO | null = null;
      try {
        const config = await this.apiClient.getLeagueConfig(leagueId);
        if (config.form?.scoring) {
          // Map form scoring to LeagueScoringConfigDTO if possible, or just use partial
          scoringConfig = {
            leagueId,
            seasonId: '', // Not available in this context
            gameId: '',
            gameName: '',
            scoringPresetId: (config.form.scoring as any).presetId,
            dropPolicySummary: '',
            championships: [],
          };
        }
      } catch (e) {
        console.warn('Failed to fetch league scoring config', e);
      }
      
      const races: RaceDTO[] = (racesPageData.races || []).map((r) => ({
        id: r.id,
        name: `${r.track} - ${r.car}`,
        date: r.scheduledAt,
        leagueName: r.leagueName,
        status: r.status,
        strengthOfField: r.strengthOfField,
      })) as unknown as RaceDTO[];

      return Result.ok({
        league,
        owner,
        scoringConfig,
        memberships,
        races,
        sponsors: [], // Sponsors integration can be added here
      });
    } catch (error: unknown) {
      console.error('LeagueService.getLeagueDetailData failed:', error);
      // Map API error types to domain error types
      if (error instanceof ApiError) {
        const errorType = error.type;
        switch (errorType) {
          case 'NOT_FOUND':
            return Result.err({ type: 'notFound', message: error.message });
          case 'AUTH_ERROR':
            return Result.err({ type: 'unauthorized', message: error.message });
          case 'SERVER_ERROR':
            return Result.err({ type: 'serverError', message: error.message });
          default:
            return Result.err({ type: 'serverError', message: error.message || 'Failed to fetch league detail' });
        }
      }
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch league detail' });
    }
  }

  async getScheduleAdminData(leagueId: string, seasonId?: string): Promise<Result<LeagueScheduleAdminData, DomainError>> {
    try {
      const seasons = await this.apiClient.getSeasons(leagueId);
      
      if (!seasons || seasons.length === 0) {
        return Result.err({ type: 'notFound', message: 'No seasons found for league' });
      }
      
      const targetSeasonId = seasonId || (seasons.find(s => s.status === 'active')?.seasonId || seasons[0].seasonId);
      const schedule = await this.apiClient.getSchedule(leagueId, targetSeasonId);
      
      return Result.ok({
        leagueId,
        seasonId: targetSeasonId,
        seasons,
        schedule,
      });
    } catch (error: unknown) {
      console.error('LeagueService.getScheduleAdminData failed:', error);
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch schedule admin data' });
    }
  }

  async getRosterAdminData(leagueId: string): Promise<Result<LeagueRosterAdminData, DomainError>> {
    try {
      const [members, joinRequests] = await Promise.all([
        this.apiClient.getAdminRosterMembers(leagueId),
        this.apiClient.getAdminRosterJoinRequests(leagueId),
      ]);

      return Result.ok({
        leagueId,
        members,
        joinRequests,
      });
    } catch (error: unknown) {
      console.error('LeagueService.getRosterAdminData failed:', error);
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch roster data' });
    }
  }

  async getLeagueSeasons(leagueId: string): Promise<Result<LeagueSeasonSummaryDTO[], DomainError>> {
    try {
      const data = await this.apiClient.getSeasons(leagueId);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch league seasons' });
    }
  }

  async getLeagueSeasonSummaries(leagueId: string): Promise<Result<LeagueSeasonSummaryDTO[], DomainError>> {
    return this.getLeagueSeasons(leagueId);
  }

  async getAdminSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueScheduleDTO, DomainError>> {
    try {
      const data = await this.apiClient.getSchedule(leagueId, seasonId);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch admin schedule' });
    }
  }

  async publishAdminSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
    try {
      const data = await this.apiClient.publishSeasonSchedule(leagueId, seasonId);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to publish schedule' });
    }
  }

  async unpublishAdminSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
    try {
      const data = await this.apiClient.unpublishSeasonSchedule(leagueId, seasonId);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to unpublish schedule' });
    }
  }

  async createAdminScheduleRace(
    leagueId: string,
    seasonId: string,
    input: { track: string; car: string; scheduledAtIso: string },
  ): Promise<Result<CreateLeagueScheduleRaceOutputDTO, DomainError>> {
    try {
      const payload: CreateLeagueScheduleRaceInputDTO = { ...input, example: '' };
      const data = await this.apiClient.createSeasonScheduleRace(leagueId, seasonId, payload);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to create race' });
    }
  }

  async updateAdminScheduleRace(
    leagueId: string,
    seasonId: string,
    raceId: string,
    input: Partial<{ track: string; car: string; scheduledAtIso: string }>,
  ): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
    try {
      const payload: UpdateLeagueScheduleRaceInputDTO = { ...input, example: '' };
      const data = await this.apiClient.updateSeasonScheduleRace(leagueId, seasonId, raceId, payload);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to update race' });
    }
  }

  async deleteAdminScheduleRace(leagueId: string, seasonId: string, raceId: string): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
    try {
      const data = await this.apiClient.deleteSeasonScheduleRace(leagueId, seasonId, raceId);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to delete race' });
    }
  }

  // TODO wtf what a stupid method??
  async getLeagueScheduleDto(leagueId: string, seasonId: string): Promise<Result<LeagueScheduleDTO, DomainError>> {
    return this.getAdminSchedule(leagueId, seasonId);
  }

  async publishLeagueSeasonSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
    return this.publishAdminSchedule(leagueId, seasonId);
  }

  async unpublishLeagueSeasonSchedule(leagueId: string, seasonId: string): Promise<Result<LeagueSeasonSchedulePublishOutputDTO, DomainError>> {
    return this.unpublishAdminSchedule(leagueId, seasonId);
  }

  async createLeagueSeasonScheduleRace(
    leagueId: string,
    seasonId: string,
    input: CreateLeagueScheduleRaceInputDTO,
  ): Promise<Result<CreateLeagueScheduleRaceOutputDTO, DomainError>> {
    try {
      const data = await this.apiClient.createSeasonScheduleRace(leagueId, seasonId, input);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to create race' });
    }
  }

  async updateLeagueSeasonScheduleRace(
    leagueId: string,
    seasonId: string,
    raceId: string,
    input: UpdateLeagueScheduleRaceInputDTO,
  ): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
    try {
      const data = await this.apiClient.updateSeasonScheduleRace(leagueId, seasonId, raceId, input);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to update race' });
    }
  }

  async deleteLeagueSeasonScheduleRace(
    leagueId: string,
    seasonId: string,
    raceId: string,
  ): Promise<Result<LeagueScheduleRaceMutationSuccessDTO, DomainError>> {
    return this.deleteAdminScheduleRace(leagueId, seasonId, raceId);
  }

  async updateMemberRole(leagueId: string, targetDriverId: string, newRole: MembershipRole): Promise<Result<{ success: boolean }, DomainError>> {
    try {
      const dto = await this.apiClient.updateRosterMemberRole(leagueId, targetDriverId, newRole);
      return Result.ok({ success: dto.success });
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to update member role' });
    }
  }

  async getAdminRosterMembers(leagueId: string): Promise<Result<LeagueRosterMemberDTO[], DomainError>> {
    try {
      const data = await this.apiClient.getAdminRosterMembers(leagueId);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch roster members' });
    }
  }

  async getAdminRosterJoinRequests(leagueId: string): Promise<Result<LeagueRosterJoinRequestDTO[], DomainError>> {
    try {
      const data = await this.apiClient.getAdminRosterJoinRequests(leagueId);
      return Result.ok(data);
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to fetch join requests' });
    }
  }

  async approveJoinRequest(leagueId: string, joinRequestId: string): Promise<Result<{ success: boolean }, DomainError>> {
    try {
      const dto = await this.apiClient.approveRosterJoinRequest(leagueId, joinRequestId);
      return Result.ok({ success: dto.success });
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to approve join request' });
    }
  }

  async rejectJoinRequest(leagueId: string, joinRequestId: string): Promise<Result<{ success: boolean }, DomainError>> {
    try {
      const dto = await this.apiClient.rejectRosterJoinRequest(leagueId, joinRequestId);
      return Result.ok({ success: dto.success });
    } catch (error: unknown) {
      return Result.err({ type: 'serverError', message: (error as Error).message || 'Failed to reject join request' });
    }
  }

  async getLeagueDetail(): Promise<Result<never, DomainError>> {
    return Result.err({ type: 'notImplemented', message: 'League detail endpoint not implemented' });
  }

  async getLeagueDetailPageData(): Promise<Result<never, DomainError>> {
    return Result.err({ type: 'notImplemented', message: 'League detail page data endpoint not implemented' });
  }

  async getScoringPresets(): Promise<Result<never, DomainError>> {
    return Result.err({ type: 'notImplemented', message: 'Scoring presets endpoint not implemented' });
  }
}
