import Decimal from 'decimal.js';
import { UserId, SettlementStatus, SettleCurrency, Money } from '@/domain/value-objects';
import { DomainError } from '@/shared/exceptions/domain.exception';

export class SettlementOrder {
  private readonly _id: bigint;
  private readonly _userId: UserId;
  private readonly _usdtAmount: Money;
  private readonly _settleCurrency: SettleCurrency;
  private _swapTxHash: string | null;
  private _receivedAmount: Money | null;
  private _status: SettlementStatus;
  private _settledAt: Date | null;
  private readonly _createdAt: Date;

  private constructor(
    id: bigint,
    userId: UserId,
    usdtAmount: Money,
    settleCurrency: SettleCurrency,
    swapTxHash: string | null,
    receivedAmount: Money | null,
    status: SettlementStatus,
    settledAt: Date | null,
    createdAt: Date,
  ) {
    this._id = id;
    this._userId = userId;
    this._usdtAmount = usdtAmount;
    this._settleCurrency = settleCurrency;
    this._swapTxHash = swapTxHash;
    this._receivedAmount = receivedAmount;
    this._status = status;
    this._settledAt = settledAt;
    this._createdAt = createdAt;
  }

  // Getters
  get id(): bigint { return this._id; }
  get userId(): UserId { return this._userId; }
  get usdtAmount(): Money { return this._usdtAmount; }
  get settleCurrency(): SettleCurrency { return this._settleCurrency; }
  get swapTxHash(): string | null { return this._swapTxHash; }
  get receivedAmount(): Money | null { return this._receivedAmount; }
  get status(): SettlementStatus { return this._status; }
  get settledAt(): Date | null { return this._settledAt; }
  get createdAt(): Date { return this._createdAt; }
  get isPending(): boolean { return this._status === SettlementStatus.PENDING; }
  get isCompleted(): boolean { return this._status === SettlementStatus.COMPLETED; }

  static create(params: {
    userId: UserId;
    usdtAmount: Money;
    settleCurrency: SettleCurrency;
  }): SettlementOrder {
    return new SettlementOrder(
      BigInt(0), // Will be set by database
      params.userId,
      params.usdtAmount,
      params.settleCurrency,
      null,
      null,
      SettlementStatus.PENDING,
      null,
      new Date(),
    );
  }

  static reconstruct(params: {
    id: bigint;
    userId: bigint;
    usdtAmount: Decimal;
    settleCurrency: string;
    swapTxHash: string | null;
    receivedAmount: Decimal | null;
    status: string;
    settledAt: Date | null;
    createdAt: Date;
  }): SettlementOrder {
    return new SettlementOrder(
      params.id,
      UserId.create(params.userId),
      Money.USDT(params.usdtAmount),
      params.settleCurrency as SettleCurrency,
      params.swapTxHash,
      params.receivedAmount ? Money.create(params.receivedAmount, params.settleCurrency) : null,
      params.status as SettlementStatus,
      params.settledAt,
      params.createdAt,
    );
  }

  startSwapping(): void {
    if (this._status !== SettlementStatus.PENDING) {
      throw new DomainError('Only pending settlements can start swapping');
    }
    this._status = SettlementStatus.SWAPPING;
  }

  complete(swapTxHash: string, receivedAmount: Money): void {
    if (this._status !== SettlementStatus.SWAPPING && this._status !== SettlementStatus.PENDING) {
      throw new DomainError('Settlement cannot be completed in current status');
    }
    this._status = SettlementStatus.COMPLETED;
    this._swapTxHash = swapTxHash;
    this._receivedAmount = receivedAmount;
    this._settledAt = new Date();
  }

  fail(): void {
    if (this._status === SettlementStatus.COMPLETED) {
      throw new DomainError('Completed settlements cannot be marked as failed');
    }
    this._status = SettlementStatus.FAILED;
  }
}
