import { Entity } from '@core/shared/domain/Entity';
import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
import { ExternalRating } from '../value-objects/ExternalRating';
import { ExternalRatingProvenance } from '../value-objects/ExternalRatingProvenance';
import { GameKey } from '../value-objects/GameKey';
import { UserId } from '../value-objects/UserId';

export interface ExternalGameRatingProfileProps {
  userId: UserId;
  gameKey: GameKey;
  ratings: Map<string, ExternalRating>; // type -> rating
  provenance: ExternalRatingProvenance;
}

export class ExternalGameRatingProfile extends Entity<UserId> {
  private readonly _userId: UserId;
  private readonly _gameKey: GameKey;
  private _ratings: Map<string, ExternalRating>;
  private _provenance: ExternalRatingProvenance;

  private constructor(props: ExternalGameRatingProfileProps) {
    super(props.userId);
    this._userId = props.userId;
    this._gameKey = props.gameKey;
    this._ratings = props.ratings;
    this._provenance = props.provenance;
  }

  static create(props: ExternalGameRatingProfileProps): ExternalGameRatingProfile {
    if (!props.userId || !props.gameKey || !props.ratings || !props.provenance) {
      throw new IdentityDomainValidationError('All properties are required');
    }

    // Note: Empty ratings map is allowed for initial creation
    // The entity can be created with no ratings and updated later

    // Validate that all ratings match the gameKey
    for (const [type, rating] of props.ratings.entries()) {
      if (!rating.gameKey.equals(props.gameKey)) {
        throw new IdentityDomainValidationError(
          `Rating type ${type} has mismatched gameKey`
        );
      }
    }

    return new ExternalGameRatingProfile(props);
  }

  static restore(props: {
    userId: string;
    gameKey: string;
    ratings: Array<{ type: string; gameKey: string; value: number }>;
    provenance: {
      source: string;
      lastSyncedAt: Date;
      verified?: boolean;
    };
  }): ExternalGameRatingProfile {
    const userId = UserId.fromString(props.userId);
    const gameKey = GameKey.create(props.gameKey);
    
    const ratingsMap = new Map<string, ExternalRating>();
    for (const ratingData of props.ratings) {
      const ratingGameKey = GameKey.create(ratingData.gameKey);
      const rating = ExternalRating.create(ratingGameKey, ratingData.type, ratingData.value);
      ratingsMap.set(ratingData.type, rating);
    }

    const provenance = ExternalRatingProvenance.restore(props.provenance);

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

  get userId(): UserId {
    return this._userId;
  }

  get gameKey(): GameKey {
    return this._gameKey;
  }

  get ratings(): ReadonlyMap<string, ExternalRating> {
    return new Map(this._ratings);
  }

  get provenance(): ExternalRatingProvenance {
    return this._provenance;
  }

  /**
   * Update ratings and provenance with latest data
   */
  updateRatings(
    newRatings: Map<string, ExternalRating>,
    newProvenance: ExternalRatingProvenance
  ): void {
    // Validate all new ratings match the gameKey
    for (const [type, rating] of newRatings.entries()) {
      if (!rating.gameKey.equals(this._gameKey)) {
        throw new IdentityDomainValidationError(
          `Rating type ${type} has mismatched gameKey`
        );
      }
    }

    this._ratings = newRatings;
    this._provenance = newProvenance;
  }

  /**
   * Get a specific rating by type
   */
  getRatingByType(type: string): ExternalRating | undefined {
    return this._ratings.get(type);
  }

  /**
   * Get all rating types
   */
  getRatingTypes(): string[] {
    return Array.from(this._ratings.keys());
  }

  /**
   * Check if profile has any ratings
   */
  hasRatings(): boolean {
    return this._ratings.size > 0;
  }

  /**
   * Mark provenance as verified
   */
  markVerified(): void {
    this._provenance = this._provenance.markVerified();
  }

  /**
   * Update last synced timestamp
   */
  updateLastSyncedAt(date: Date): void {
    this._provenance = this._provenance.updateLastSyncedAt(date);
  }

  /**
   * Get summary for display
   */
  toSummary(): {
    userId: string;
    gameKey: string;
    ratingCount: number;
    ratingTypes: string[];
    source: string;
    lastSyncedAt: Date;
    verified: boolean;
  } {
    return {
      userId: this._userId.toString(),
      gameKey: this._gameKey.toString(),
      ratingCount: this._ratings.size,
      ratingTypes: this.getRatingTypes(),
      source: this._provenance.source,
      lastSyncedAt: this._provenance.lastSyncedAt,
      verified: this._provenance.verified,
    };
  }

  /**
   * Serialize for storage
   */
  toJSON(): {
    userId: string;
    gameKey: string;
    ratings: Array<{ type: string; gameKey: string; value: number }>;
    provenance: {
      source: string;
      lastSyncedAt: Date;
      verified: boolean;
    };
  } {
    return {
      userId: this._userId.toString(),
      gameKey: this._gameKey.toString(),
      ratings: Array.from(this._ratings.entries()).map(([type, rating]) => ({
        type,
        gameKey: rating.gameKey.toString(),
        value: rating.value,
      })),
      provenance: {
        source: this._provenance.source,
        lastSyncedAt: this._provenance.lastSyncedAt,
        verified: this._provenance.verified,
      },
    };
  }

  equals(other: ExternalGameRatingProfile): boolean {
    if (!(other instanceof ExternalGameRatingProfile)) {
      return false;
    }

    if (!this._userId.equals(other._userId)) {
      return false;
    }

    if (!this._gameKey.equals(other._gameKey)) {
      return false;
    }

    if (!this._provenance.equals(other._provenance)) {
      return false;
    }

    // Compare ratings maps
    if (this._ratings.size !== other._ratings.size) {
      return false;
    }

    for (const [type, rating] of this._ratings.entries()) {
      const otherRating = other._ratings.get(type);
      if (!otherRating || !rating.equals(otherRating)) {
        return false;
      }
    }

    return true;
  }
}