import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { OrderAggregate, OrderType, OrderStatus, OrderSource } from '../../../domain/aggregates/order.aggregate';
import { Money } from '../../../domain/value-objects/money.vo';

@Injectable()
export class OrderRepository {
  constructor(private readonly prisma: PrismaService) {}

  async findById(id: string): Promise<OrderAggregate | null> {
    const record = await this.prisma.order.findUnique({ where: { id } });
    if (!record) return null;
    return this.toDomain(record);
  }

  async findByOrderNo(orderNo: string): Promise<OrderAggregate | null> {
    const record = await this.prisma.order.findUnique({ where: { orderNo } });
    if (!record) return null;
    return this.toDomain(record);
  }

  async save(aggregate: OrderAggregate): Promise<string> {
    const data = {
      orderNo: aggregate.orderNo,
      accountSequence: aggregate.accountSequence,
      type: aggregate.type,
      status: aggregate.status,
      source: aggregate.source,
      sourceLabel: aggregate.sourceLabel,
      price: aggregate.price.value,
      quantity: aggregate.quantity.value,
      filledQuantity: aggregate.filledQuantity.value,
      remainingQuantity: aggregate.remainingQuantity.value,
      averagePrice: aggregate.averagePrice.value,
      totalAmount: aggregate.totalAmount.value,
      cancelledAt: aggregate.cancelledAt,
      completedAt: aggregate.completedAt,
    };

    if (aggregate.id) {
      await this.prisma.order.update({
        where: { id: aggregate.id },
        data,
      });
      return aggregate.id;
    } else {
      const created = await this.prisma.order.create({ data });
      return created.id;
    }
  }

  /**
   * 保存订单并更新销毁信息（用于卖出订单）
   */
  async saveWithBurnInfo(
    aggregate: OrderAggregate,
    burnQuantity: Money,
    effectiveQuantity: Money,
  ): Promise<string> {
    const data = {
      orderNo: aggregate.orderNo,
      accountSequence: aggregate.accountSequence,
      type: aggregate.type,
      status: aggregate.status,
      source: aggregate.source,
      sourceLabel: aggregate.sourceLabel,
      price: aggregate.price.value,
      quantity: aggregate.quantity.value,
      filledQuantity: aggregate.filledQuantity.value,
      remainingQuantity: aggregate.remainingQuantity.value,
      averagePrice: aggregate.averagePrice.value,
      totalAmount: aggregate.totalAmount.value,
      burnQuantity: burnQuantity.value,
      burnMultiplier: burnQuantity.isZero()
        ? 0
        : burnQuantity.value.dividedBy(aggregate.filledQuantity.value),
      effectiveQuantity: effectiveQuantity.value,
      cancelledAt: aggregate.cancelledAt,
      completedAt: aggregate.completedAt,
    };

    if (aggregate.id) {
      await this.prisma.order.update({
        where: { id: aggregate.id },
        data,
      });
      return aggregate.id;
    } else {
      const created = await this.prisma.order.create({ data });
      return created.id;
    }
  }

  async findActiveOrders(type?: OrderType): Promise<OrderAggregate[]> {
    const where: any = {
      status: { in: [OrderStatus.PENDING, OrderStatus.PARTIAL] },
    };
    if (type) where.type = type;

    const records = await this.prisma.order.findMany({
      where,
      orderBy: [{ price: type === OrderType.BUY ? 'desc' : 'asc' }, { createdAt: 'asc' }],
    });

    return records.map((r) => this.toDomain(r));
  }

  async findByAccountSequence(
    accountSequence: string,
    options?: { status?: OrderStatus; page?: number; pageSize?: number },
  ): Promise<{ data: OrderAggregate[]; total: number }> {
    const where: any = { accountSequence };
    if (options?.status) where.status = options.status;

    const page = options?.page ?? 1;
    const pageSize = options?.pageSize ?? 50;

    const [records, total] = await Promise.all([
      this.prisma.order.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.order.count({ where }),
    ]);

    return {
      data: records.map((r) => this.toDomain(r)),
      total,
    };
  }

  async getOrderBook(limit: number = 20): Promise<{
    bids: { price: string; quantity: string; count: number }[];
    asks: { price: string; quantity: string; count: number }[];
  }> {
    const [bids, asks] = await Promise.all([
      this.prisma.order.groupBy({
        by: ['price'],
        where: { type: OrderType.BUY, status: { in: [OrderStatus.PENDING, OrderStatus.PARTIAL] } },
        _sum: { remainingQuantity: true },
        _count: { id: true },
        orderBy: { price: 'desc' },
        take: limit,
      }),
      this.prisma.order.groupBy({
        by: ['price'],
        where: { type: OrderType.SELL, status: { in: [OrderStatus.PENDING, OrderStatus.PARTIAL] } },
        _sum: { remainingQuantity: true },
        _count: { id: true },
        orderBy: { price: 'asc' },
        take: limit,
      }),
    ]);

    return {
      bids: bids.map((b) => ({
        price: b.price.toString(),
        quantity: (b._sum.remainingQuantity || 0).toString(),
        count: b._count.id,
      })),
      asks: asks.map((a) => ({
        price: a.price.toString(),
        quantity: (a._sum.remainingQuantity || 0).toString(),
        count: a._count.id,
      })),
    };
  }

  /**
   * 查找最老的待成交卖单（用于做市商吃单）
   */
  async findOldestPendingSellOrder(): Promise<OrderAggregate | null> {
    const record = await this.prisma.order.findFirst({
      where: {
        type: OrderType.SELL,
        status: { in: [OrderStatus.PENDING, OrderStatus.PARTIAL] },
      },
      orderBy: { createdAt: 'asc' },
    });
    if (!record) return null;
    return this.toDomain(record);
  }

  /**
   * 按来源查询订单
   */
  async findBySource(
    source: OrderSource,
    options?: { type?: OrderType; status?: OrderStatus; page?: number; pageSize?: number },
  ): Promise<{ data: OrderAggregate[]; total: number }> {
    const where: any = { source };
    if (options?.type) where.type = options.type;
    if (options?.status) where.status = options.status;

    const page = options?.page ?? 1;
    const pageSize = options?.pageSize ?? 50;

    const [records, total] = await Promise.all([
      this.prisma.order.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.order.count({ where }),
    ]);

    return {
      data: records.map((r) => this.toDomain(r)),
      total,
    };
  }

  /**
   * 查询用户的成交记录（作为买方或卖方）
   * 返回完整明细：包括销毁倍数、有效积分股、手续费（进入积分股池）等
   */
  async findTradesByAccountSequence(
    accountSequence: string,
    options?: { page?: number; pageSize?: number },
  ): Promise<{
    data: Array<{
      id: string;
      tradeNo: string;
      type: 'BUY' | 'SELL';
      price: string;
      quantity: string; // 有效积分股（含销毁倍数）
      originalQuantity: string; // 原始卖出数量
      burnQuantity: string; // 销毁数量
      burnMultiplier: string; // 销毁倍数
      grossAmount: string; // 交易总额（扣除手续费前）
      fee: string; // 手续费（进入积分股池）
      netAmount: string; // 实际获得（扣除手续费后）
      counterparty: string;
      createdAt: Date;
    }>;
    total: number;
  }> {
    const page = options?.page ?? 1;
    const pageSize = options?.pageSize ?? 50;

    // 查询作为买方和卖方的成交记录
    const [buyerTrades, sellerTrades, buyerCount, sellerCount] = await Promise.all([
      this.prisma.trade.findMany({
        where: { buyerSequence: accountSequence },
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.trade.findMany({
        where: { sellerSequence: accountSequence },
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.trade.count({ where: { buyerSequence: accountSequence } }),
      this.prisma.trade.count({ where: { sellerSequence: accountSequence } }),
    ]);

    // 合并并转换格式
    const allTrades = [
      ...buyerTrades.map((t) => {
        // 买方：quantity 是有效积分股，originalQuantity 是原始数量
        // 买方支付金额 = 原始数量 × 价格
        const originalQty = t.originalQuantity?.toString() || t.quantity.toString();
        const buyerPayAmount = Number(originalQty) * Number(t.price);
        return {
          id: t.id,
          tradeNo: t.tradeNo,
          type: 'BUY' as const,
          price: t.price.toString(),
          quantity: t.quantity.toString(), // 有效积分股（买方获得的积分股）
          originalQuantity: originalQty, // 原始数量
          burnQuantity: '0',
          burnMultiplier: '1', // 买方无销毁倍数
          grossAmount: buyerPayAmount.toString(), // 买方支付总额
          fee: '0', // 买方不支付手续费
          netAmount: buyerPayAmount.toString(), // 买方支付总额
          counterparty: t.sellerSequence,
          createdAt: t.createdAt,
        };
      }),
      ...sellerTrades.map((t) => {
        // 卖方：计算销毁倍数和原始数量
        const effectiveQty = Number(t.quantity); // 有效积分股（含销毁倍数）
        const burnQty = Number(t.burnQuantity);
        // 原始数量：优先使用数据库值，如果为0则通过 有效量 - 销毁量 计算
        // 注意：使用 != null 而不是 || 来正确处理0值
        let originalQty = t.originalQuantity != null && Number(t.originalQuantity) > 0
          ? Number(t.originalQuantity)
          : effectiveQty - burnQty;
        // 确保原始数量不为负数
        if (originalQty <= 0) originalQty = effectiveQty;
        // 销毁倍数 = 有效积分股 / 原始数量
        const burnMultiplier = originalQty > 0 ? effectiveQty / originalQty : 1;
        // 交易总额 = 有效积分股 × 价格
        const grossAmount = effectiveQty * Number(t.price);
        // 实际获得 = 交易总额 - 手续费 (即 amount 字段)
        return {
          id: t.id,
          tradeNo: t.tradeNo,
          type: 'SELL' as const,
          price: t.price.toString(),
          quantity: t.quantity.toString(), // 有效积分股
          originalQuantity: originalQty.toString(), // 原始卖出数量
          burnQuantity: t.burnQuantity.toString(), // 销毁数量
          burnMultiplier: burnMultiplier.toString(), // 销毁倍数
          grossAmount: grossAmount.toString(), // 交易总额
          fee: t.fee.toString(), // 手续费（进入积分股池）
          netAmount: t.amount.toString(), // 实际获得（扣除手续费后）
          counterparty: t.buyerSequence,
          createdAt: t.createdAt,
        };
      }),
    ];

    // 按时间排序
    allTrades.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());

    // 分页
    const paged = allTrades.slice((page - 1) * pageSize, page * pageSize);

    return {
      data: paged,
      total: buyerCount + sellerCount,
    };
  }

  /**
   * 全局订单查询（管理后台用）
   */
  async findAllOrders(options?: {
    type?: OrderType;
    status?: OrderStatus;
    source?: OrderSource;
    search?: string;
    startDate?: Date;
    endDate?: Date;
    page?: number;
    pageSize?: number;
  }): Promise<{ data: any[]; total: number }> {
    const where: any = {};

    if (options?.type) where.type = options.type;
    if (options?.status) where.status = options.status;
    if (options?.source) where.source = options.source;

    if (options?.search) {
      where.OR = [
        { accountSequence: { contains: options.search } },
        { orderNo: { contains: options.search } },
      ];
    }

    if (options?.startDate || options?.endDate) {
      where.createdAt = {};
      if (options?.startDate) where.createdAt.gte = options.startDate;
      if (options?.endDate) where.createdAt.lte = options.endDate;
    }

    const page = options?.page ?? 1;
    const pageSize = options?.pageSize ?? 20;

    const [records, total] = await Promise.all([
      this.prisma.order.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.order.count({ where }),
    ]);

    return {
      data: records.map((o) => ({
        id: o.id,
        orderNo: o.orderNo,
        accountSequence: o.accountSequence,
        type: o.type,
        status: o.status,
        source: o.source,
        sourceLabel: o.sourceLabel,
        price: o.price.toString(),
        quantity: o.quantity.toString(),
        filledQuantity: o.filledQuantity.toString(),
        remainingQuantity: o.remainingQuantity.toString(),
        averagePrice: o.averagePrice.toString(),
        totalAmount: o.totalAmount.toString(),
        burnQuantity: o.burnQuantity.toString(),
        burnMultiplier: o.burnMultiplier.toString(),
        effectiveQuantity: o.effectiveQuantity.toString(),
        createdAt: o.createdAt,
        completedAt: o.completedAt,
        cancelledAt: o.cancelledAt,
      })),
      total,
    };
  }

  /**
   * 全局成交记录查询（管理后台用）
   */
  async findAllTrades(options?: {
    source?: string;
    search?: string;
    startDate?: Date;
    endDate?: Date;
    page?: number;
    pageSize?: number;
  }): Promise<{ data: any[]; total: number }> {
    const where: any = {};

    if (options?.source) {
      where.OR = [
        { buyerSource: options.source },
        { sellerSource: options.source },
      ];
    }

    if (options?.search) {
      const searchCondition = {
        OR: [
          { buyerSequence: { contains: options.search } },
          { sellerSequence: { contains: options.search } },
          { tradeNo: { contains: options.search } },
        ],
      };
      if (where.OR) {
        where.AND = [{ OR: where.OR }, searchCondition];
        delete where.OR;
      } else {
        where.OR = searchCondition.OR;
      }
    }

    if (options?.startDate || options?.endDate) {
      where.createdAt = {};
      if (options?.startDate) where.createdAt.gte = options.startDate;
      if (options?.endDate) where.createdAt.lte = options.endDate;
    }

    const page = options?.page ?? 1;
    const pageSize = options?.pageSize ?? 20;

    const [records, total] = await Promise.all([
      this.prisma.trade.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.trade.count({ where }),
    ]);

    return {
      data: records.map((t) => ({
        id: t.id,
        tradeNo: t.tradeNo,
        buyOrderId: t.buyOrderId,
        sellOrderId: t.sellOrderId,
        buyerSequence: t.buyerSequence,
        sellerSequence: t.sellerSequence,
        price: t.price.toString(),
        quantity: t.quantity.toString(),
        originalQuantity: t.originalQuantity.toString(),
        burnQuantity: t.burnQuantity.toString(),
        amount: t.amount.toString(),
        fee: t.fee.toString(),
        buyerSource: t.buyerSource,
        sellerSource: t.sellerSource,
        createdAt: t.createdAt,
      })),
      total,
    };
  }

  private toDomain(record: any): OrderAggregate {
    return OrderAggregate.reconstitute({
      id: record.id,
      orderNo: record.orderNo,
      accountSequence: record.accountSequence,
      type: record.type as OrderType,
      status: record.status as OrderStatus,
      source: (record.source as OrderSource) || OrderSource.USER,
      sourceLabel: record.sourceLabel,
      price: new Money(record.price),
      quantity: new Money(record.quantity),
      filledQuantity: new Money(record.filledQuantity),
      remainingQuantity: new Money(record.remainingQuantity),
      averagePrice: new Money(record.averagePrice),
      totalAmount: new Money(record.totalAmount),
      createdAt: record.createdAt,
      cancelledAt: record.cancelledAt,
      completedAt: record.completedAt,
    });
  }
}
