import { HttpStatus } from '@nestjs/common';
import { type OrderStatus } from '@prisma/client';
import { AggregateRoot, DomainException, ErrorCode, Result } from '@modules/shared';
import { OrderCancelledEvent } from '../events/order-cancelled.event';
import { OrderCreatedEvent } from '../events/order-created.event';
import { OrderPaidEvent } from '../events/order-paid.event';
import { type Money } from '../value-objects/money.vo';
import { type PlatformFee } from '../value-objects/platform-fee.vo';

export interface OrderProps {
  buyerId: string;
  sellerId: string;
  listingId: string;
  status: OrderStatus;
  amount: Money;
  platformFee: PlatformFee;
  sellerPayout: Money;
  idempotencyKey: string | null;
  metadata: unknown;
}

/** Allowed status transitions for the order state machine. */
const VALID_TRANSITIONS: Partial<Record<OrderStatus, OrderStatus[]>> = {
  CREATED: ['PAYMENT_PENDING', 'CANCELLED'],
  PAYMENT_PENDING: ['PAYMENT_CONFIRMED', 'CANCELLED'],
  PAYMENT_CONFIRMED: ['ESCROW_HELD', 'REFUNDED'],
  ESCROW_HELD: ['SHIPPED', 'DISPUTE', 'REFUNDED'],
  SHIPPED: ['DELIVERED', 'DISPUTE'],
  DELIVERED: ['ESCROW_RELEASED', 'DISPUTE'],
  DISPUTE: ['ESCROW_RELEASED', 'REFUNDED'],
  ESCROW_RELEASED: ['COMPLETED'],
};

export class OrderEntity extends AggregateRoot<string> {
  private _buyerId: string;
  private _sellerId: string;
  private _listingId: string;
  private _status: OrderStatus;
  private _amount: Money;
  private _platformFee: PlatformFee;
  private _sellerPayout: Money;
  private _idempotencyKey: string | null;
  private _metadata: unknown;

  constructor(id: string, props: OrderProps, createdAt?: Date, updatedAt?: Date) {
    super(id, createdAt, updatedAt);
    this._buyerId = props.buyerId;
    this._sellerId = props.sellerId;
    this._listingId = props.listingId;
    this._status = props.status;
    this._amount = props.amount;
    this._platformFee = props.platformFee;
    this._sellerPayout = props.sellerPayout;
    this._idempotencyKey = props.idempotencyKey;
    this._metadata = props.metadata;
  }

  get buyerId(): string { return this._buyerId; }
  get sellerId(): string { return this._sellerId; }
  get listingId(): string { return this._listingId; }
  get status(): OrderStatus { return this._status; }
  get amount(): Money { return this._amount; }
  get platformFee(): PlatformFee { return this._platformFee; }
  get sellerPayout(): Money { return this._sellerPayout; }
  get idempotencyKey(): string | null { return this._idempotencyKey; }
  get metadata(): unknown { return this._metadata; }

  static createNew(
    id: string,
    buyerId: string,
    sellerId: string,
    listingId: string,
    amount: Money,
    platformFee: PlatformFee,
    sellerPayout: Money,
    idempotencyKey?: string,
  ): OrderEntity {
    const order = new OrderEntity(id, {
      buyerId,
      sellerId,
      listingId,
      status: 'CREATED',
      amount,
      platformFee,
      sellerPayout,
      idempotencyKey: idempotencyKey ?? null,
      metadata: null,
    });

    order.addDomainEvent(
      new OrderCreatedEvent(id, buyerId, sellerId, listingId, amount.value),
    );

    return order;
  }

  /** Transition the order to a new status, enforcing the state machine. */
  private transitionTo(newStatus: OrderStatus): Result<void, DomainException> {
    const allowed = VALID_TRANSITIONS[this._status];
    if (!allowed || !allowed.includes(newStatus)) {
      return Result.err(
        new DomainException(
          ErrorCode.ORDER_INVALID_STATUS_TRANSITION,
          `Không thể chuyển đơn hàng từ ${this._status} sang ${newStatus}`,
          HttpStatus.CONFLICT,
        ),
      );
    }
    this._status = newStatus;
    this.updatedAt = new Date();
    return Result.ok(undefined);
  }

  markPaymentPending(): Result<void, DomainException> {
    return this.transitionTo('PAYMENT_PENDING');
  }

  markPaymentConfirmed(): Result<void, DomainException> {
    const result = this.transitionTo('PAYMENT_CONFIRMED');
    if (result.isOk) {
      this.addDomainEvent(
        new OrderPaidEvent(this.id, this._buyerId, this._amount.value),
      );
    }
    return result;
  }

  markEscrowHeld(): Result<void, DomainException> {
    return this.transitionTo('ESCROW_HELD');
  }

  markShipped(): Result<void, DomainException> {
    return this.transitionTo('SHIPPED');
  }

  markDelivered(): Result<void, DomainException> {
    return this.transitionTo('DELIVERED');
  }

  markEscrowReleased(): Result<void, DomainException> {
    return this.transitionTo('ESCROW_RELEASED');
  }

  markCompleted(): Result<void, DomainException> {
    return this.transitionTo('COMPLETED');
  }

  markDispute(): Result<void, DomainException> {
    return this.transitionTo('DISPUTE');
  }

  markCancelled(): Result<void, DomainException> {
    const result = this.transitionTo('CANCELLED');
    if (result.isOk) {
      this.addDomainEvent(
        new OrderCancelledEvent(this.id, this._buyerId, this._sellerId),
      );
    }
    return result;
  }

  markRefunded(): Result<void, DomainException> {
    return this.transitionTo('REFUNDED');
  }
}
