import type { IEntity } from '@core/shared/domain';
import { RatingEventId } from '../value-objects/RatingEventId';
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
import { RatingDelta } from '../value-objects/RatingDelta';
import { IdentityDomainValidationError, IdentityDomainInvariantError } from '../errors/IdentityDomainError';

export interface RatingEventSource {
  type: 'race' | 'penalty' | 'vote' | 'adminAction' | 'manualAdjustment';
  id: string;
}

export interface RatingEventReason {
  code: string;
  summary: string;
  details: Record<string, unknown>;
}

export interface RatingEventVisibility {
  public: boolean;
  redactedFields: string[];
}

export interface RatingEventProps {
  id: RatingEventId;
  userId: string;
  dimension: RatingDimensionKey;
  delta: RatingDelta;
  weight?: number;
  occurredAt: Date;
  createdAt: Date;
  source: RatingEventSource;
  reason: RatingEventReason;
  visibility: RatingEventVisibility;
  version: number;
}

export class RatingEvent implements IEntity<RatingEventId> {
  readonly id: RatingEventId;
  readonly userId: string;
  readonly dimension: RatingDimensionKey;
  readonly delta: RatingDelta;
  readonly weight: number | undefined;
  readonly occurredAt: Date;
  readonly createdAt: Date;
  readonly source: RatingEventSource;
  readonly reason: RatingEventReason;
  readonly visibility: RatingEventVisibility;
  readonly version: number;

  private constructor(props: RatingEventProps) {
    this.id = props.id;
    this.userId = props.userId;
    this.dimension = props.dimension;
    this.delta = props.delta;
    this.weight = props.weight;
    this.occurredAt = props.occurredAt;
    this.createdAt = props.createdAt;
    this.source = props.source;
    this.reason = props.reason;
    this.visibility = props.visibility;
    this.version = props.version;
  }

  static create(props: RatingEventProps): RatingEvent {
    // Validate required fields
    if (!props.userId || props.userId.trim().length === 0) {
      throw new IdentityDomainValidationError('userId is required');
    }

    if (!props.dimension) {
      throw new IdentityDomainValidationError('dimension is required');
    }

    if (!props.delta) {
      throw new IdentityDomainValidationError('delta is required');
    }

    if (!props.source) {
      throw new IdentityDomainValidationError('source is required');
    }

    if (!props.reason) {
      throw new IdentityDomainValidationError('reason is required');
    }

    if (!props.visibility) {
      throw new IdentityDomainValidationError('visibility is required');
    }

    if (!props.version || props.version < 1) {
      throw new IdentityDomainValidationError('version must be a positive integer');
    }

    // Validate dates
    const now = new Date();
    if (props.occurredAt > now) {
      throw new IdentityDomainValidationError('occurredAt cannot be in the future');
    }

    if (props.createdAt > now) {
      throw new IdentityDomainValidationError('createdAt cannot be in the future');
    }

    if (props.occurredAt > props.createdAt) {
      throw new IdentityDomainInvariantError('occurredAt must be before or equal to createdAt');
    }

    // Validate weight if provided
    if (props.weight !== undefined && (props.weight <= 0 || !Number.isFinite(props.weight))) {
      throw new IdentityDomainValidationError('weight must be a positive number');
    }

    return new RatingEvent(props);
  }

  static rehydrate(props: RatingEventProps): RatingEvent {
    // Rehydration assumes data is already validated (from persistence)
    return new RatingEvent(props);
  }

  equals(other: IEntity<RatingEventId>): boolean {
    return this.id.equals(other.id);
  }

  toJSON(): Record<string, unknown> {
    return {
      id: this.id.value,
      userId: this.userId,
      dimension: this.dimension.value,
      delta: this.delta.value,
      weight: this.weight,
      occurredAt: this.occurredAt.toISOString(),
      createdAt: this.createdAt.toISOString(),
      source: this.source,
      reason: this.reason,
      visibility: this.visibility,
      version: this.version,
    };
  }
}