import { IExternalGameRatingRepository } from '../../domain/repositories/IExternalGameRatingRepository';
import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
import { UserId } from '../../domain/value-objects/UserId';
import { GameKey } from '../../domain/value-objects/GameKey';
import { ExternalRating } from '../../domain/value-objects/ExternalRating';
import { ExternalRatingProvenance } from '../../domain/value-objects/ExternalRatingProvenance';
import { UpsertExternalGameRatingInput, UpsertExternalGameRatingOutput } from '../dtos/UpsertExternalGameRatingDto';

/**
 * Use Case: UpsertExternalGameRatingUseCase
 * 
 * Upserts external game rating profile with latest data and provenance.
 * Follows CQRS Light: command side operation.
 * 
 * Store/display only, no compute.
 * 
 * Flow:
 * 1. Validate input
 * 2. Check if profile exists
 * 3. Create or update profile with latest ratings
 * 4. Save to repository
 * 5. Return result
 */
export class UpsertExternalGameRatingUseCase {
  constructor(
    private readonly externalGameRatingRepository: IExternalGameRatingRepository
  ) {}

  async execute(input: UpsertExternalGameRatingInput): Promise<UpsertExternalGameRatingOutput> {
    try {
      // 1. Validate input
      const validationError = this.validateInput(input);
      if (validationError) {
        return {
          success: false,
          profile: {
            userId: input.userId,
            gameKey: input.gameKey,
            ratingCount: 0,
            ratingTypes: [],
            source: input.provenance.source,
            lastSyncedAt: input.provenance.lastSyncedAt,
            verified: input.provenance.verified ?? false,
          },
          action: 'created',
          errors: [validationError],
        };
      }

      // 2. Check if profile exists
      const existingProfile = await this.externalGameRatingRepository.findByUserIdAndGameKey(
        input.userId,
        input.gameKey
      );

      // 3. Create or update profile
      let profile: ExternalGameRatingProfile;
      let action: 'created' | 'updated';

      if (existingProfile) {
        // Update existing profile
        const ratingsMap = this.createRatingsMap(input.ratings, input.gameKey);
        const provenance = ExternalRatingProvenance.create({
          source: input.provenance.source,
          lastSyncedAt: new Date(input.provenance.lastSyncedAt),
          verified: input.provenance.verified ?? false,
        });

        existingProfile.updateRatings(ratingsMap, provenance);
        profile = existingProfile;
        action = 'updated';
      } else {
        // Create new profile
        profile = this.createProfile(input);
        action = 'created';
      }

      // 4. Save to repository
      const savedProfile = await this.externalGameRatingRepository.save(profile);

      // 5. Return result
      const summary = savedProfile.toSummary();
      return {
        success: true,
        profile: {
          userId: summary.userId,
          gameKey: summary.gameKey,
          ratingCount: summary.ratingCount,
          ratingTypes: summary.ratingTypes,
          source: summary.source,
          lastSyncedAt: summary.lastSyncedAt.toISOString(),
          verified: summary.verified,
        },
        action,
      };

    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      return {
        success: false,
        profile: {
          userId: input.userId,
          gameKey: input.gameKey,
          ratingCount: 0,
          ratingTypes: [],
          source: input.provenance.source,
          lastSyncedAt: input.provenance.lastSyncedAt,
          verified: input.provenance.verified ?? false,
        },
        action: 'created',
        errors: [errorMessage],
      };
    }
  }

  private validateInput(input: UpsertExternalGameRatingInput): string | null {
    if (!input.userId || input.userId.trim().length === 0) {
      return 'User ID is required';
    }

    if (!input.gameKey || input.gameKey.trim().length === 0) {
      return 'Game key is required';
    }

    if (!input.ratings || input.ratings.length === 0) {
      return 'Ratings are required';
    }

    for (const rating of input.ratings) {
      if (!rating.type || rating.type.trim().length === 0) {
        return 'Rating type cannot be empty';
      }
      if (typeof rating.value !== 'number' || isNaN(rating.value)) {
        return `Rating value for type ${rating.type} must be a valid number`;
      }
    }

    if (!input.provenance.source || input.provenance.source.trim().length === 0) {
      return 'Provenance source is required';
    }

    const syncDate = new Date(input.provenance.lastSyncedAt);
    if (isNaN(syncDate.getTime())) {
      return 'Provenance lastSyncedAt must be a valid date';
    }

    return null;
  }

  private createRatingsMap(
    ratingsData: Array<{ type: string; value: number }>,
    gameKeyString: string
  ): Map<string, ExternalRating> {
    const gameKey = GameKey.create(gameKeyString);
    const ratingsMap = new Map<string, ExternalRating>();

    for (const ratingData of ratingsData) {
      const rating = ExternalRating.create(
        gameKey,
        ratingData.type.trim(),
        ratingData.value
      );
      ratingsMap.set(ratingData.type.trim(), rating);
    }

    return ratingsMap;
  }

  private createProfile(input: UpsertExternalGameRatingInput): ExternalGameRatingProfile {
    const userId = UserId.fromString(input.userId);
    const gameKey = GameKey.create(input.gameKey);
    const ratingsMap = this.createRatingsMap(input.ratings, input.gameKey);
    const provenance = ExternalRatingProvenance.create({
      source: input.provenance.source,
      lastSyncedAt: new Date(input.provenance.lastSyncedAt),
      verified: input.provenance.verified ?? false,
    });

    return ExternalGameRatingProfile.create({
      userId,
      gameKey,
      ratings: ratingsMap,
      provenance,
    });
  }
}