import { Injectable, Logger } from '@nestjs/common';
import { OrderRepository } from '../../infrastructure/persistence/repositories/order.repository';
import { TradingAccountRepository } from '../../infrastructure/persistence/repositories/trading-account.repository';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { RedisService } from '../../infrastructure/redis/redis.service';
import { OrderAggregate, OrderType, OrderStatus } from '../../domain/aggregates/order.aggregate';
import { TradingAccountAggregate } from '../../domain/aggregates/trading-account.aggregate';
import { MatchingEngineService } from '../../domain/services/matching-engine.service';
import { Money } from '../../domain/value-objects/money.vo';

@Injectable()
export class OrderService {
  private readonly logger = new Logger(OrderService.name);
  private readonly matchingEngine = new MatchingEngineService();

  constructor(
    private readonly orderRepository: OrderRepository,
    private readonly accountRepository: TradingAccountRepository,
    private readonly prisma: PrismaService,
    private readonly redis: RedisService,
  ) {}

  async createOrder(
    accountSequence: string,
    type: OrderType,
    price: string,
    quantity: string,
  ): Promise<{ orderId: string; orderNo: string; status: OrderStatus; filledQuantity: string }> {
    const lockValue = await this.redis.acquireLock(`order:create:${accountSequence}`, 10);
    if (!lockValue) {
      throw new Error('System busy, please try again');
    }

    try {
      // 验证账户
      let account = await this.accountRepository.findByAccountSequence(accountSequence);
      if (!account) {
        account = TradingAccountAggregate.create(accountSequence);
      }

      const priceAmount = new Money(price);
      const quantityAmount = new Money(quantity);
      const totalCost = quantityAmount.multiply(priceAmount.value);

      // 检查余额并冻结
      if (type === OrderType.BUY) {
        if (account.availableCash.isLessThan(totalCost)) {
          throw new Error('Insufficient cash balance');
        }
      } else {
        if (account.availableShares.isLessThan(quantityAmount)) {
          throw new Error('Insufficient share balance');
        }
      }

      // 生成订单号
      const orderNo = this.generateOrderNo();

      // 创建订单
      const order = OrderAggregate.create(orderNo, accountSequence, type, priceAmount, quantityAmount);

      // 冻结资产
      if (type === OrderType.BUY) {
        account.freezeCash(totalCost, orderNo);
      } else {
        account.freezeShares(quantityAmount, orderNo);
      }

      // 保存订单和账户
      const orderId = await this.orderRepository.save(order);
      await this.accountRepository.save(account);

      // 尝试撮合
      await this.tryMatch(order);

      // 获取最新订单状态
      const updatedOrder = await this.orderRepository.findByOrderNo(orderNo);

      return {
        orderId,
        orderNo,
        status: updatedOrder?.status || order.status,
        filledQuantity: updatedOrder?.filledQuantity.toString() || '0',
      };
    } finally {
      await this.redis.releaseLock(`order:create:${accountSequence}`, lockValue);
    }
  }

  async cancelOrder(accountSequence: string, orderNo: string): Promise<void> {
    const order = await this.orderRepository.findByOrderNo(orderNo);
    if (!order) {
      throw new Error('Order not found');
    }
    if (order.accountSequence !== accountSequence) {
      throw new Error('Unauthorized');
    }

    const account = await this.accountRepository.findByAccountSequence(accountSequence);
    if (!account) {
      throw new Error('Account not found');
    }

    // 取消订单
    order.cancel();

    // 解冻资产
    if (order.type === OrderType.BUY) {
      account.unfreezeCash(order.lockedAmount, orderNo);
    } else {
      account.unfreezeShares(order.remainingQuantity, orderNo);
    }

    await this.orderRepository.save(order);
    await this.accountRepository.save(account);
  }

  private async tryMatch(incomingOrder: OrderAggregate): Promise<void> {
    const lockValue = await this.redis.acquireLock('order:matching', 30);
    if (!lockValue) return;

    try {
      const oppositeType = incomingOrder.type === OrderType.BUY ? OrderType.SELL : OrderType.BUY;
      const orderBook = await this.orderRepository.findActiveOrders(oppositeType);

      const matches = this.matchingEngine.findMatchingOrders(incomingOrder, orderBook);

      for (const match of matches) {
        // 保存成交记录
        await this.prisma.trade.create({
          data: {
            tradeNo: match.trade.tradeNo,
            buyOrderId: match.buyOrder.id!,
            sellOrderId: match.sellOrder.id!,
            buyerSequence: match.buyOrder.accountSequence,
            sellerSequence: match.sellOrder.accountSequence,
            price: match.trade.price.value,
            quantity: match.trade.quantity.value,
            amount: match.trade.amount.value,
          },
        });

        // 更新订单
        await this.orderRepository.save(match.buyOrder);
        await this.orderRepository.save(match.sellOrder);

        // 更新买方账户
        const buyerAccount = await this.accountRepository.findByAccountSequence(match.buyOrder.accountSequence);
        if (buyerAccount) {
          buyerAccount.executeBuy(match.trade.quantity, match.trade.amount, match.trade.tradeNo);
          await this.accountRepository.save(buyerAccount);
        }

        // 更新卖方账户
        const sellerAccount = await this.accountRepository.findByAccountSequence(match.sellOrder.accountSequence);
        if (sellerAccount) {
          sellerAccount.executeSell(match.trade.quantity, match.trade.amount, match.trade.tradeNo);
          await this.accountRepository.save(sellerAccount);
        }

        this.logger.log(
          `Trade executed: ${match.trade.tradeNo}, price=${match.trade.price}, qty=${match.trade.quantity}`,
        );
      }
    } finally {
      await this.redis.releaseLock('order:matching', lockValue);
    }
  }

  private generateOrderNo(): string {
    const timestamp = Date.now().toString(36);
    const random = Math.random().toString(36).substring(2, 8);
    return `O${timestamp}${random}`.toUpperCase();
  }
}
