import { DomainEvent } from '../../events/domain-event.base';
import { RewardCreatedEvent } from '../../events/reward-created.event';
import { RewardClaimedEvent } from '../../events/reward-claimed.event';
import { RewardExpiredEvent } from '../../events/reward-expired.event';
import { RewardSettledEvent } from '../../events/reward-settled.event';
import { RewardSource } from '../../value-objects/reward-source.vo';
import { RewardStatus } from '../../value-objects/reward-status.enum';
import { Money } from '../../value-objects/money.vo';
import { Hashpower } from '../../value-objects/hashpower.vo';

/**
 * 奖励流水聚合根
 *
 * 不变式:
 * 1. 待领取奖励必须在24小时内认种，否则过期
 * 2. 只有待领取状态才能领取(claim)
 * 3. 只有可结算状态才能结算(settle)
 * 4. 已结算/已过期的奖励状态不可变更
 */
export class RewardLedgerEntry {
  private _id: bigint | null = null;
  private readonly _userId: bigint;
  private readonly _accountSequence: string;
  private readonly _rewardSource: RewardSource;
  private readonly _usdtAmount: Money;
  private readonly _hashpowerAmount: Hashpower;
  private _rewardStatus: RewardStatus;
  private readonly _createdAt: Date;
  private _expireAt: Date | null;
  private _claimedAt: Date | null;
  private _settledAt: Date | null;
  private _expiredAt: Date | null;
  private readonly _memo: string;

  private _domainEvents: DomainEvent[] = [];

  private constructor(
    userId: bigint,
    accountSequence: string,
    rewardSource: RewardSource,
    usdtAmount: Money,
    hashpowerAmount: Hashpower,
    rewardStatus: RewardStatus,
    createdAt: Date,
    expireAt: Date | null,
    memo: string,
  ) {
    this._userId = userId;
    this._accountSequence = accountSequence;
    this._rewardSource = rewardSource;
    this._usdtAmount = usdtAmount;
    this._hashpowerAmount = hashpowerAmount;
    this._rewardStatus = rewardStatus;
    this._createdAt = createdAt;
    this._expireAt = expireAt;
    this._claimedAt = null;
    this._settledAt = null;
    this._expiredAt = null;
    this._memo = memo;
  }

  // ============ Getters ============
  get id(): bigint | null { return this._id; }
  get userId(): bigint { return this._userId; }
  get accountSequence(): string { return this._accountSequence; }
  get rewardSource(): RewardSource { return this._rewardSource; }
  get usdtAmount(): Money { return this._usdtAmount; }
  get hashpowerAmount(): Hashpower { return this._hashpowerAmount; }
  get rewardStatus(): RewardStatus { return this._rewardStatus; }
  get createdAt(): Date { return this._createdAt; }
  get expireAt(): Date | null { return this._expireAt; }
  get claimedAt(): Date | null { return this._claimedAt; }
  get settledAt(): Date | null { return this._settledAt; }
  get expiredAt(): Date | null { return this._expiredAt; }
  get memo(): string { return this._memo; }
  get domainEvents(): DomainEvent[] { return [...this._domainEvents]; }

  get isPending(): boolean { return this._rewardStatus === RewardStatus.PENDING; }
  get isSettleable(): boolean { return this._rewardStatus === RewardStatus.SETTLEABLE; }
  get isSettled(): boolean { return this._rewardStatus === RewardStatus.SETTLED; }
  get isExpired(): boolean { return this._rewardStatus === RewardStatus.EXPIRED; }

  // ============ 工厂方法 ============

  /**
   * 创建待领取奖励 (24小时倒计时)
   * 用于推荐人未认种的情况
   */
  static createPending(params: {
    userId: bigint;
    accountSequence: string;
    rewardSource: RewardSource;
    usdtAmount: Money;
    hashpowerAmount: Hashpower;
    memo?: string;
  }): RewardLedgerEntry {
    const now = new Date();
    const expireAt = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24小时后

    const entry = new RewardLedgerEntry(
      params.userId,
      params.accountSequence,
      params.rewardSource,
      params.usdtAmount,
      params.hashpowerAmount,
      RewardStatus.PENDING,
      now,
      expireAt,
      params.memo || '',
    );

    entry._domainEvents.push(new RewardCreatedEvent({
      entryId: entry._id?.toString() || 'temp',
      userId: entry._userId.toString(),
      sourceOrderNo: entry._rewardSource.sourceOrderNo,
      sourceUserId: entry._rewardSource.sourceUserId.toString(),
      rightType: entry._rewardSource.rightType,
      usdtAmount: entry._usdtAmount.amount,
      hashpowerAmount: entry._hashpowerAmount.value,
      rewardStatus: entry._rewardStatus,
      expireAt: entry._expireAt,
    }));

    return entry;
  }

  /**
   * 创建直接可结算奖励 (无需24小时等待)
   * 用于推荐人已认种的情况
   */
  static createSettleable(params: {
    userId: bigint;
    accountSequence: string;
    rewardSource: RewardSource;
    usdtAmount: Money;
    hashpowerAmount: Hashpower;
    memo?: string;
  }): RewardLedgerEntry {
    const entry = new RewardLedgerEntry(
      params.userId,
      params.accountSequence,
      params.rewardSource,
      params.usdtAmount,
      params.hashpowerAmount,
      RewardStatus.SETTLEABLE,
      new Date(),
      null,
      params.memo || '',
    );

    entry._domainEvents.push(new RewardCreatedEvent({
      entryId: entry._id?.toString() || 'temp',
      userId: entry._userId.toString(),
      sourceOrderNo: entry._rewardSource.sourceOrderNo,
      sourceUserId: entry._rewardSource.sourceUserId.toString(),
      rightType: entry._rewardSource.rightType,
      usdtAmount: entry._usdtAmount.amount,
      hashpowerAmount: entry._hashpowerAmount.value,
      rewardStatus: entry._rewardStatus,
      expireAt: null,
    }));

    return entry;
  }

  // ============ 领域行为 ============

  /**
   * 领取奖励 (用户认种后，待领取 → 可结算)
   */
  claim(): void {
    if (this._rewardStatus !== RewardStatus.PENDING) {
      throw new Error('只有待领取状态才能领取');
    }

    if (this.isExpiredNow()) {
      throw new Error('奖励已过期，无法领取');
    }

    this._rewardStatus = RewardStatus.SETTLEABLE;
    this._claimedAt = new Date();
    this._expireAt = null;

    this._domainEvents.push(new RewardClaimedEvent({
      entryId: this._id?.toString() || '',
      userId: this._userId.toString(),
      usdtAmount: this._usdtAmount.amount,
      hashpowerAmount: this._hashpowerAmount.value,
    }));
  }

  /**
   * 过期 (24小时后未认种)
   */
  expire(): void {
    if (this._rewardStatus !== RewardStatus.PENDING) {
      throw new Error('只有待领取状态才能过期');
    }

    this._rewardStatus = RewardStatus.EXPIRED;
    this._expiredAt = new Date();

    this._domainEvents.push(new RewardExpiredEvent({
      entryId: this._id?.toString() || '',
      userId: this._userId.toString(),
      usdtAmount: this._usdtAmount.amount,
      hashpowerAmount: this._hashpowerAmount.value,
      transferredTo: 'HEADQUARTERS_COMMUNITY',
    }));
  }

  /**
   * 结算 (可结算 → 已结算)
   */
  settle(settleCurrency: string, receivedAmount: number): void {
    if (this._rewardStatus !== RewardStatus.SETTLEABLE) {
      throw new Error('只有可结算状态才能结算');
    }

    this._rewardStatus = RewardStatus.SETTLED;
    this._settledAt = new Date();

    this._domainEvents.push(new RewardSettledEvent({
      entryId: this._id?.toString() || '',
      userId: this._userId.toString(),
      usdtAmount: this._usdtAmount.amount,
      hashpowerAmount: this._hashpowerAmount.value,
      settleCurrency,
      receivedAmount,
    }));
  }

  /**
   * 检查是否已过期
   */
  isExpiredNow(): boolean {
    if (!this._expireAt) return false;
    return new Date() > this._expireAt;
  }

  /**
   * 获取剩余过期时间 (毫秒)
   */
  getRemainingTimeMs(): number {
    if (!this._expireAt) return 0;
    const remaining = this._expireAt.getTime() - Date.now();
    return Math.max(0, remaining);
  }

  setId(id: bigint): void {
    this._id = id;
  }

  clearDomainEvents(): void {
    this._domainEvents = [];
  }

  // ============ 重建 ============

  static reconstitute(data: {
    id: bigint;
    userId: bigint;
    accountSequence: string;
    rewardSource: RewardSource;
    usdtAmount: number;
    hashpowerAmount: number;
    rewardStatus: RewardStatus;
    createdAt: Date;
    expireAt: Date | null;
    claimedAt: Date | null;
    settledAt: Date | null;
    expiredAt: Date | null;
    memo: string;
  }): RewardLedgerEntry {
    const entry = new RewardLedgerEntry(
      data.userId,
      data.accountSequence,
      data.rewardSource,
      Money.USDT(data.usdtAmount),
      Hashpower.create(data.hashpowerAmount),
      data.rewardStatus,
      data.createdAt,
      data.expireAt,
      data.memo,
    );
    entry._id = data.id;
    entry._claimedAt = data.claimedAt;
    entry._settledAt = data.settledAt;
    entry._expiredAt = data.expiredAt;
    return entry;
  }
}
