import { IRatingEventRepository } from '../../domain/repositories/IRatingEventRepository';
import { IUserRatingRepository } from '../../domain/repositories/IUserRatingRepository';
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
import { RatingEvent } from '../../domain/entities/RatingEvent';
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
import { CreateRatingEventDto } from '../dtos/CreateRatingEventDto';

/**
 * Input for AppendRatingEventsUseCase
 */
export interface AppendRatingEventsInput {
  userId: string;
  events?: CreateRatingEventDto[]; // Optional: direct event creation
  // Alternative: raceId, penaltyId, etc. for factory-based creation
  raceId?: string;
  raceResults?: Array<{
    position: number;
    totalDrivers: number;
    startPosition: number;
    incidents: number;
    fieldStrength: number;
    status: 'finished' | 'dnf' | 'dns' | 'dsq' | 'afk';
  }>;
}

/**
 * Output for AppendRatingEventsUseCase
 */
export interface AppendRatingEventsOutput {
  events: string[]; // Event IDs
  snapshotUpdated: boolean;
}

/**
 * Use Case: AppendRatingEventsUseCase
 * 
 * Appends rating events to the ledger and recomputes the snapshot.
 * Follows CQRS Light: command side operation.
 */
export class AppendRatingEventsUseCase {
  constructor(
    private readonly ratingEventRepository: IRatingEventRepository,
    private readonly userRatingRepository: IUserRatingRepository,
  ) {}

  async execute(input: AppendRatingEventsInput): Promise<AppendRatingEventsOutput> {
    const eventsToSave = [];

    // 1. Create events from direct input
    if (input.events) {
      for (const eventDto of input.events) {
        const event = this.createEventFromDto(eventDto);
        eventsToSave.push(event);
      }
    }

    // 2. Create events from race results (using factory)
    if (input.raceId && input.raceResults) {
      for (const result of input.raceResults) {
        const raceEvents = RatingEventFactory.createFromRaceFinish({
          userId: input.userId,
          raceId: input.raceId,
          position: result.position,
          totalDrivers: result.totalDrivers,
          startPosition: result.startPosition,
          incidents: result.incidents,
          fieldStrength: result.fieldStrength,
          status: result.status,
        });
        eventsToSave.push(...raceEvents);
      }
    }

    // 3. Save all events to ledger
    const savedEventIds: string[] = [];
    for (const event of eventsToSave) {
      await this.ratingEventRepository.save(event);
      savedEventIds.push(event.id.value);
    }

    // 4. Recompute snapshot if events were saved
    let snapshotUpdated = false;
    if (savedEventIds.length > 0) {
      const allEvents = await this.ratingEventRepository.getAllByUserId(input.userId);
      const snapshot = RatingSnapshotCalculator.calculate(input.userId, allEvents);
      await this.userRatingRepository.save(snapshot);
      snapshotUpdated = true;
    }

    return {
      events: savedEventIds,
      snapshotUpdated,
    };
  }

  private createEventFromDto(dto: CreateRatingEventDto) {
    const props: any = {
      id: RatingEventId.generate(),
      userId: dto.userId,
      dimension: RatingDimensionKey.create(dto.dimension),
      delta: RatingDelta.create(dto.delta),
      occurredAt: dto.occurredAt ? new Date(dto.occurredAt) : new Date(),
      createdAt: new Date(),
      source: { type: dto.sourceType, id: dto.sourceId },
      reason: {
        code: dto.reasonCode,
        summary: dto.reasonSummary,
        details: dto.reasonDetails || {},
      },
      visibility: { public: true, redactedFields: [] },
      version: 1,
    };
    
    if (dto.weight !== undefined) {
      props.weight = dto.weight;
    }
    
    return RatingEvent.create(props);
  }
}