/**
 * Domain Entity: Transaction
 *
 * Represents a financial transaction in the league wallet system.
 */

import { RacingDomainValidationError, RacingDomainInvariantError } from '../../errors/RacingDomainError';

import type { Money } from '../../value-objects/Money';
import type { IEntity } from '@core/shared/domain';
import type { TransactionId } from './TransactionId';
import type { LeagueWalletId } from './LeagueWalletId';

export type TransactionType =
  | 'sponsorship_payment'
  | 'membership_payment'
  | 'prize_payout'
  | 'withdrawal'
  | 'refund';

export type TransactionStatus = 'pending' | 'completed' | 'failed' | 'cancelled';

export interface TransactionProps {
  id: TransactionId;
  walletId: LeagueWalletId;
  type: TransactionType;
  amount: Money;
  platformFee: Money;
  netAmount: Money;
  status: TransactionStatus;
  createdAt: Date;
  completedAt: Date | undefined;
  description: string | undefined;
  metadata: Record<string, unknown> | undefined;
}

export class Transaction implements IEntity<TransactionId> {
  readonly id: TransactionId;
  readonly walletId: LeagueWalletId;
  readonly type: TransactionType;
  readonly amount: Money;
  readonly platformFee: Money;
  readonly netAmount: Money;
  readonly status: TransactionStatus;
  readonly createdAt: Date;
  readonly completedAt: Date | undefined;
  readonly description: string | undefined;
  readonly metadata: Record<string, unknown> | undefined;

  private constructor(props: TransactionProps) {
    this.id = props.id;
    this.walletId = props.walletId;
    this.type = props.type;
    this.amount = props.amount;
    this.platformFee = props.platformFee;
    this.netAmount = props.netAmount;
    this.status = props.status;
    this.createdAt = props.createdAt;
    this.completedAt = props.completedAt;
    this.description = props.description;
    this.metadata = props.metadata;
  }

  static create(props: Omit<TransactionProps, 'createdAt' | 'status' | 'platformFee' | 'netAmount'> & {
    createdAt?: Date;
    status?: TransactionStatus;
  }): Transaction {
    this.validate(props);

    const platformFee = props.amount.calculatePlatformFee();
    const netAmount = props.amount.calculateNetAmount();

    return new Transaction({
      ...props,
      platformFee,
      netAmount,
      createdAt: props.createdAt ?? new Date(),
      status: props.status ?? 'pending',
    });
  }

  private static validate(props: Omit<TransactionProps, 'createdAt' | 'status' | 'platformFee' | 'netAmount'>): void {
    if (!props.id) {
      throw new RacingDomainValidationError('Transaction ID is required');
    }

    if (!props.walletId) {
      throw new RacingDomainValidationError('Transaction walletId is required');
    }

    if (!props.type) {
      throw new RacingDomainValidationError('Transaction type is required');
    }

    if (!props.amount) {
      throw new RacingDomainValidationError('Transaction amount is required');
    }

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

  /**
   * Mark transaction as completed
   */
  complete(): Transaction {
    if (this.status === 'completed') {
      throw new RacingDomainInvariantError('Transaction is already completed');
    }

    if (this.status === 'failed' || this.status === 'cancelled') {
      throw new RacingDomainInvariantError('Cannot complete a failed or cancelled transaction');
    }

    return new Transaction({
      ...this,
      status: 'completed',
      completedAt: new Date(),
    });
  }

  /**
   * Mark transaction as failed
   */
  fail(): Transaction {
    if (this.status === 'completed') {
      throw new RacingDomainInvariantError('Cannot fail a completed transaction');
    }

    return new Transaction({
      ...this,
      status: 'failed',
    });
  }

  /**
   * Cancel transaction
   */
  cancel(): Transaction {
    if (this.status === 'completed') {
      throw new RacingDomainInvariantError('Cannot cancel a completed transaction');
    }

    return new Transaction({
      ...this,
      status: 'cancelled',
    });
  }

  /**
   * Check if transaction is completed
   */
  isCompleted(): boolean {
    return this.status === 'completed';
  }

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