/**
 * Domain Entity: Prize
 *
 * Represents a prize awarded to a driver for a specific position in a season.
 */

import { Entity } from '@core/shared/domain/Entity';
import { RacingDomainInvariantError, RacingDomainValidationError } from '../../errors/RacingDomainError';

import type { Money } from '../../value-objects/Money';
import { Position } from '../championship/Position';
import { DriverId } from '../DriverId';
import { SeasonId } from '../season/SeasonId';
import { PrizeId } from './PrizeId';
import { PrizeStatus } from './PrizeStatus';

export interface PrizeProps {
  id: PrizeId;
  seasonId: SeasonId;
  position: Position;
  amount: Money;
  driverId: DriverId | undefined;
  status: PrizeStatus;
  createdAt: Date;
  awardedAt: Date | undefined;
  paidAt: Date | undefined;
  description: string | undefined;
}

export class Prize extends Entity<PrizeId> {
  readonly seasonId: SeasonId;
  readonly position: Position;
  readonly amount: Money;
  readonly driverId: DriverId | undefined;
  readonly status: PrizeStatus;
  readonly createdAt: Date;
  readonly awardedAt: Date | undefined;
  readonly paidAt: Date | undefined;
  readonly description: string | undefined;

  private constructor(props: PrizeProps) {
    super(props.id);
    
    this.seasonId = props.seasonId;
    this.position = props.position;
    this.amount = props.amount;
    this.driverId = props.driverId;
    this.status = props.status;
    this.createdAt = props.createdAt;
    this.awardedAt = props.awardedAt;
    this.paidAt = props.paidAt;
    this.description = props.description;
  }

  static create(props: Omit<PrizeProps, 'createdAt' | 'status' | 'driverId' | 'awardedAt' | 'paidAt' | 'description' | 'id' | 'seasonId' | 'position'> & {
    id: string;
    seasonId: string;
    position: number;
    createdAt?: Date;
    status?: string;
    driverId?: string;
    awardedAt?: Date;
    paidAt?: Date;
    description?: string;
  }): Prize {
    const fullProps: Omit<PrizeProps, 'createdAt' | 'status'> = {
      id: PrizeId.create(props.id),
      seasonId: SeasonId.create(props.seasonId),
      position: Position.create(props.position),
      amount: props.amount,
      driverId: props.driverId ? DriverId.create(props.driverId) : undefined,
      awardedAt: props.awardedAt,
      paidAt: props.paidAt,
      description: props.description,
    };

    this.validate(fullProps);

    return new Prize({
      ...fullProps,
      createdAt: props.createdAt ?? new Date(),
      status: PrizeStatus.create(props.status ?? 'pending'),
    });
  }

  private static validate(props: Omit<PrizeProps, 'createdAt' | 'status'>): void {
    if (!props.amount) {
      throw new RacingDomainValidationError('Prize amount is required');
    }

    if (props.amount.amount <= 0) {
      throw new RacingDomainValidationError('Prize amount must be greater than zero');
    }
  }

  /**
   * Award prize to a driver
   */
  awardTo(driverId: string): Prize {
    if (!driverId || driverId.trim().length === 0) {
      throw new RacingDomainValidationError('Driver ID is required to award prize');
    }

    if (this.status.toString() !== 'pending') {
      throw new RacingDomainInvariantError('Only pending prizes can be awarded');
    }

    return new Prize({
      ...this,
      driverId: DriverId.create(driverId),
      status: PrizeStatus.create('awarded'),
      awardedAt: new Date(),
    });
  }

  /**
   * Mark prize as paid
   */
  markAsPaid(): Prize {
    if (this.status.toString() !== 'awarded') {
      throw new RacingDomainInvariantError('Only awarded prizes can be marked as paid');
    }

    if (!this.driverId) {
      throw new RacingDomainInvariantError('Prize must have a driver to be paid');
    }

    return new Prize({
      ...this,
      status: PrizeStatus.create('paid'),
      paidAt: new Date(),
    });
  }

  /**
   * Cancel prize
   */
  cancel(): Prize {
    if (this.status.toString() === 'paid') {
      throw new RacingDomainInvariantError('Cannot cancel a paid prize');
    }

    return new Prize({
      ...this,
      status: PrizeStatus.create('cancelled'),
    });
  }

  /**
   * Check if prize is pending
   */
  isPending(): boolean {
    return this.status.toString() === 'pending';
  }

  /**
   * Check if prize is awarded
   */
  isAwarded(): boolean {
    return this.status.toString() === 'awarded';
  }

  /**
   * Check if prize is paid
   */
  isPaid(): boolean {
    return this.status.toString() === 'paid';
  }
}