import { PlantingOrderStatus } from '../value-objects/planting-order-status.enum';
import { ProvinceCitySelection } from '../value-objects/province-city-selection.vo';
import { FundAllocation } from '../value-objects/fund-allocation.vo';
import { TreeCount } from '../value-objects/tree-count.vo';
import { PRICE_PER_TREE } from '../value-objects/fund-allocation-target-type.enum';
import { DomainEvent } from '../events/domain-event.interface';
import { PlantingOrderCreatedEvent } from '../events/planting-order-created.event';
import { ProvinceCityConfirmedEvent } from '../events/province-city-confirmed.event';
import { PlantingOrderPaidEvent } from '../events/planting-order-paid.event';
import { FundsAllocatedEvent } from '../events/funds-allocated.event';
import { PoolInjectedEvent } from '../events/pool-injected.event';
import { MiningEnabledEvent } from '../events/mining-enabled.event';

export interface PlantingOrderData {
  id?: bigint;
  orderNo: string;
  userId: bigint;
  accountSequence: string;
  treeCount: number;
  totalAmount: number;
  priceSupplement?: number; // 总部运营成本压力涨价，归总部 (S0000000001)
  status: PlantingOrderStatus;
  selectedProvince?: string | null;
  selectedCity?: string | null;
  provinceCitySelectedAt?: Date | null;
  provinceCityConfirmedAt?: Date | null;
  poolInjectionBatchId?: bigint | null;
  poolInjectionScheduledTime?: Date | null;
  poolInjectionActualTime?: Date | null;
  poolInjectionTxHash?: string | null;
  miningEnabledAt?: Date | null;
  createdAt?: Date;
  paidAt?: Date | null;
  fundAllocatedAt?: Date | null;
}

export class PlantingOrder {
  private _id: bigint | null;
  private readonly _orderNo: string;
  private readonly _userId: bigint;
  private readonly _accountSequence: string;
  private readonly _treeCount: TreeCount;
  private readonly _totalAmount: number;
  private readonly _priceSupplement: number; // 总部运营成本压力涨价
  private _provinceCitySelection: ProvinceCitySelection | null;
  private _status: PlantingOrderStatus;
  private _fundAllocations: FundAllocation[];
  private _poolInjectionBatchId: bigint | null;
  private _poolInjectionScheduledTime: Date | null;
  private _poolInjectionActualTime: Date | null;
  private _poolInjectionTxHash: string | null;
  private _miningEnabledAt: Date | null;
  private readonly _createdAt: Date;
  private _paidAt: Date | null;
  private _fundAllocatedAt: Date | null;

  // 领域事件
  private _domainEvents: DomainEvent[] = [];

  private constructor(
    orderNo: string,
    userId: bigint,
    accountSequence: string,
    treeCount: TreeCount,
    totalAmount: number,
    priceSupplement: number = 0,
    createdAt?: Date,
  ) {
    this._id = null;
    this._orderNo = orderNo;
    this._userId = userId;
    this._accountSequence = accountSequence;
    this._treeCount = treeCount;
    this._totalAmount = totalAmount;
    this._priceSupplement = priceSupplement;
    this._status = PlantingOrderStatus.CREATED;
    this._provinceCitySelection = null;
    this._fundAllocations = [];
    this._poolInjectionBatchId = null;
    this._poolInjectionScheduledTime = null;
    this._poolInjectionActualTime = null;
    this._poolInjectionTxHash = null;
    this._miningEnabledAt = null;
    this._createdAt = createdAt || new Date();
    this._paidAt = null;
    this._fundAllocatedAt = null;
  }

  // Getters
  get id(): bigint | null {
    return this._id;
  }
  get orderNo(): string {
    return this._orderNo;
  }
  get userId(): bigint {
    return this._userId;
  }
  get accountSequence(): string {
    return this._accountSequence;
  }
  get treeCount(): TreeCount {
    return this._treeCount;
  }
  get totalAmount(): number {
    return this._totalAmount;
  }
  /** 总部运营成本压力涨价（每棵树加价金额），归总部 (S0000000001) */
  get priceSupplement(): number {
    return this._priceSupplement;
  }
  get status(): PlantingOrderStatus {
    return this._status;
  }
  get provinceCitySelection(): ProvinceCitySelection | null {
    return this._provinceCitySelection;
  }
  get fundAllocations(): ReadonlyArray<FundAllocation> {
    return this._fundAllocations;
  }
  get poolInjectionBatchId(): bigint | null {
    return this._poolInjectionBatchId;
  }
  get poolInjectionScheduledTime(): Date | null {
    return this._poolInjectionScheduledTime;
  }
  get poolInjectionActualTime(): Date | null {
    return this._poolInjectionActualTime;
  }
  get poolInjectionTxHash(): string | null {
    return this._poolInjectionTxHash;
  }
  get miningEnabledAt(): Date | null {
    return this._miningEnabledAt;
  }
  get isMiningEnabled(): boolean {
    return this._miningEnabledAt !== null;
  }
  get createdAt(): Date {
    return this._createdAt;
  }
  get paidAt(): Date | null {
    return this._paidAt;
  }
  get fundAllocatedAt(): Date | null {
    return this._fundAllocatedAt;
  }
  get domainEvents(): ReadonlyArray<DomainEvent> {
    return this._domainEvents;
  }

  /**
   * 工厂方法：创建认种订单
   * @param priceSupplement 总部运营成本压力涨价（每棵树加价金额），默认 0 不影响现有逻辑
   */
  static create(
    userId: bigint,
    accountSequence: string,
    treeCount: number,
    priceSupplement: number = 0,
  ): PlantingOrder {
    if (treeCount <= 0) {
      throw new Error('认种数量必须大于0');
    }

    const orderNo = `PLT${Date.now()}${Math.random().toString(36).substring(2, 8).toUpperCase()}`;
    const tree = TreeCount.create(treeCount);
    const totalAmount = treeCount * (PRICE_PER_TREE + priceSupplement);

    const order = new PlantingOrder(orderNo, userId, accountSequence, tree, totalAmount, priceSupplement);

    // 发布领域事件
    order._domainEvents.push(
      new PlantingOrderCreatedEvent(orderNo, {
        orderNo: order.orderNo,
        userId: order.userId.toString(),
        accountSequence: order.accountSequence,
        treeCount: order.treeCount.value,
        totalAmount: order.totalAmount,
      }),
    );

    return order;
  }

  /**
   * 选择省市 (5秒倒计时前)
   */
  selectProvinceCity(
    provinceCode: string,
    provinceName: string,
    cityCode: string,
    cityName: string,
  ): void {
    this.ensureStatus(PlantingOrderStatus.CREATED);

    if (this._provinceCitySelection?.isConfirmed) {
      throw new Error('省市已确认，不可修改');
    }

    this._provinceCitySelection = ProvinceCitySelection.create(
      provinceCode,
      provinceName,
      cityCode,
      cityName,
    );
  }

  /**
   * 确认省市选择 (5秒后)
   */
  confirmProvinceCity(): void {
    this.ensureStatus(PlantingOrderStatus.CREATED);

    if (!this._provinceCitySelection) {
      throw new Error('请先选择省市');
    }

    if (!this._provinceCitySelection.canConfirm()) {
      throw new Error('请等待5秒确认时间');
    }

    this._provinceCitySelection = this._provinceCitySelection.confirm();
    this._status = PlantingOrderStatus.PROVINCE_CITY_CONFIRMED;

    this._domainEvents.push(
      new ProvinceCityConfirmedEvent(this.orderNo, {
        orderNo: this.orderNo,
        userId: this.userId.toString(),
        provinceCode: this._provinceCitySelection.provinceCode,
        provinceName: this._provinceCitySelection.provinceName,
        cityCode: this._provinceCitySelection.cityCode,
        cityName: this._provinceCitySelection.cityName,
      }),
    );
  }

  /**
   * 标记为已支付
   */
  markAsPaid(): void {
    this.ensureStatus(PlantingOrderStatus.PROVINCE_CITY_CONFIRMED);

    this._status = PlantingOrderStatus.PAID;
    this._paidAt = new Date();

    this._domainEvents.push(
      new PlantingOrderPaidEvent(this.orderNo, {
        orderNo: this.orderNo,
        userId: this.userId.toString(),
        accountSequence: this._accountSequence,
        treeCount: this.treeCount.value,
        totalAmount: this.totalAmount,
        provinceCode: this._provinceCitySelection!.provinceCode,
        cityCode: this._provinceCitySelection!.cityCode,
      }),
    );
  }

  /**
   * 分配资金
   */
  allocateFunds(allocations: FundAllocation[]): void {
    this.ensureStatus(PlantingOrderStatus.PAID);

    // 验证分配总额
    const totalAllocated = allocations.reduce((sum, a) => sum + a.amount, 0);
    if (Math.abs(totalAllocated - this.totalAmount) > 0.01) {
      throw new Error(
        `资金分配总额不匹配: 期望 ${this.totalAmount}, 实际 ${totalAllocated}`,
      );
    }

    this._fundAllocations = allocations;
    this._status = PlantingOrderStatus.FUND_ALLOCATED;
    this._fundAllocatedAt = new Date();

    this._domainEvents.push(
      new FundsAllocatedEvent(this.orderNo, {
        orderNo: this.orderNo,
        allocations: allocations.map((a) => a.toDTO()),
      }),
    );
  }

  /**
   * 安排底池注入
   * 注意：资金分配已移至 reward-service 异步处理，所以支付后即可安排底池注入
   */
  schedulePoolInjection(batchId: bigint, scheduledTime: Date): void {
    this.ensureStatus(PlantingOrderStatus.PAID);

    this._poolInjectionBatchId = batchId;
    this._poolInjectionScheduledTime = scheduledTime;
    this._status = PlantingOrderStatus.POOL_SCHEDULED;
  }

  /**
   * 确认底池注入完成
   */
  confirmPoolInjection(txHash: string): void {
    this.ensureStatus(PlantingOrderStatus.POOL_SCHEDULED);

    this._poolInjectionActualTime = new Date();
    this._poolInjectionTxHash = txHash;
    this._status = PlantingOrderStatus.POOL_INJECTED;

    this._domainEvents.push(
      new PoolInjectedEvent(this.orderNo, {
        orderNo: this.orderNo,
        userId: this.userId.toString(),
        amount: this.treeCount.value * 800, // 800 USDT/棵
        txHash,
      }),
    );
  }

  /**
   * 开启挖矿
   * 支付完成后直接开启，跳过底池注入流程
   */
  enableMining(): void {
    this.ensureStatus(
      PlantingOrderStatus.PAID,
      PlantingOrderStatus.POOL_SCHEDULED,
      PlantingOrderStatus.POOL_INJECTED,
    );

    this._miningEnabledAt = new Date();
    this._status = PlantingOrderStatus.MINING_ENABLED;

    this._domainEvents.push(
      new MiningEnabledEvent(this.orderNo, {
        orderNo: this.orderNo,
        userId: this.userId.toString(),
        treeCount: this.treeCount.value,
      }),
    );
  }

  /**
   * 取消订单
   */
  cancel(): void {
    if (
      this._status !== PlantingOrderStatus.CREATED &&
      this._status !== PlantingOrderStatus.PROVINCE_CITY_CONFIRMED
    ) {
      throw new Error('只有未支付的订单才能取消');
    }
    this._status = PlantingOrderStatus.CANCELLED;
  }

  /**
   * 清除领域事件
   */
  clearDomainEvents(): void {
    this._domainEvents = [];
  }

  private ensureStatus(...allowedStatuses: PlantingOrderStatus[]): void {
    if (!allowedStatuses.includes(this._status)) {
      throw new Error(
        `订单状态错误: 当前 ${this._status}, 期望 ${allowedStatuses.join(' 或 ')}`,
      );
    }
  }

  /**
   * 设置ID（用于持久化后回填）
   */
  setId(id: bigint): void {
    if (this._id !== null) {
      throw new Error('ID已设置，不可修改');
    }
    this._id = id;
  }

  /**
   * 用于从数据库重建
   */
  static reconstitute(data: PlantingOrderData): PlantingOrder {
    const order = new PlantingOrder(
      data.orderNo,
      data.userId,
      data.accountSequence,
      TreeCount.create(data.treeCount),
      data.totalAmount,
      data.priceSupplement || 0,
      data.createdAt,
    );

    if (data.id) {
      order._id = data.id;
    }
    order._status = data.status;
    order._paidAt = data.paidAt || null;
    order._fundAllocatedAt = data.fundAllocatedAt || null;
    order._poolInjectionBatchId = data.poolInjectionBatchId || null;
    order._poolInjectionScheduledTime = data.poolInjectionScheduledTime || null;
    order._poolInjectionActualTime = data.poolInjectionActualTime || null;
    order._poolInjectionTxHash = data.poolInjectionTxHash || null;
    order._miningEnabledAt = data.miningEnabledAt || null;

    if (data.selectedProvince && data.selectedCity) {
      order._provinceCitySelection = ProvinceCitySelection.reconstitute(
        data.selectedProvince,
        '',
        data.selectedCity,
        '',
        data.provinceCitySelectedAt || new Date(),
        data.provinceCityConfirmedAt || null,
      );
    }

    return order;
  }

  /**
   * 转换为可持久化的数据对象
   */
  toPersistence(): PlantingOrderData {
    return {
      id: this._id || undefined,
      orderNo: this._orderNo,
      userId: this._userId,
      accountSequence: this._accountSequence,
      treeCount: this._treeCount.value,
      totalAmount: this._totalAmount,
      priceSupplement: this._priceSupplement,
      status: this._status,
      selectedProvince: this._provinceCitySelection?.provinceCode || null,
      selectedCity: this._provinceCitySelection?.cityCode || null,
      provinceCitySelectedAt: this._provinceCitySelection?.selectedAt || null,
      provinceCityConfirmedAt: this._provinceCitySelection?.confirmedAt || null,
      poolInjectionBatchId: this._poolInjectionBatchId,
      poolInjectionScheduledTime: this._poolInjectionScheduledTime,
      poolInjectionActualTime: this._poolInjectionActualTime,
      poolInjectionTxHash: this._poolInjectionTxHash,
      miningEnabledAt: this._miningEnabledAt,
      createdAt: this._createdAt,
      paidAt: this._paidAt,
      fundAllocatedAt: this._fundAllocatedAt,
    };
  }
}
