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,
    };
  }

  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,
    });
  }
}
