import { Injectable } from '@nestjs/common';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import { IDepositOrderRepository } from '@/domain/repositories';
import { DepositOrder } from '@/domain/aggregates';
import { DepositStatus } from '@/domain/value-objects';
import Decimal from 'decimal.js';

@Injectable()
export class DepositOrderRepositoryImpl implements IDepositOrderRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(order: DepositOrder): Promise<DepositOrder> {
    const data = {
      accountSequence: order.accountSequence,
      userId: order.userId.value,
      chainType: order.chainType,
      amount: order.amount.toDecimal(),
      txHash: order.txHash,
      status: order.status,
      confirmedAt: order.confirmedAt,
    };

    if (order.id === BigInt(0)) {
      const created = await this.prisma.depositOrder.create({ data });
      return this.toDomain(created);
    } else {
      const updated = await this.prisma.depositOrder.update({
        where: { id: order.id },
        data,
      });
      return this.toDomain(updated);
    }
  }

  async findById(orderId: bigint): Promise<DepositOrder | null> {
    const record = await this.prisma.depositOrder.findUnique({
      where: { id: orderId },
    });
    return record ? this.toDomain(record) : null;
  }

  async findByTxHash(txHash: string): Promise<DepositOrder | null> {
    const record = await this.prisma.depositOrder.findUnique({
      where: { txHash },
    });
    return record ? this.toDomain(record) : null;
  }

  async findByUserId(userId: bigint, status?: DepositStatus): Promise<DepositOrder[]> {
    const where: Record<string, unknown> = { userId };
    if (status) {
      where.status = status;
    }

    const records = await this.prisma.depositOrder.findMany({
      where,
      orderBy: { createdAt: 'desc' },
    });
    return records.map(r => this.toDomain(r));
  }

  async existsByTxHash(txHash: string): Promise<boolean> {
    const count = await this.prisma.depositOrder.count({
      where: { txHash },
    });
    return count > 0;
  }

  async findByAccountSequence(accountSequence: string, status?: DepositStatus): Promise<DepositOrder[]> {
    const where: Record<string, unknown> = { accountSequence };
    if (status) {
      where.status = status;
    }

    const records = await this.prisma.depositOrder.findMany({
      where,
      orderBy: { createdAt: 'desc' },
    });
    return records.map(r => this.toDomain(r));
  }

  private toDomain(record: {
    id: bigint;
    accountSequence: string;
    userId: bigint;
    chainType: string;
    amount: Decimal;
    txHash: string;
    status: string;
    confirmedAt: Date | null;
    createdAt: Date;
  }): DepositOrder {
    return DepositOrder.reconstruct({
      id: record.id,
      accountSequence: record.accountSequence,
      userId: record.userId,
      chainType: record.chainType,
      amount: new Decimal(record.amount.toString()),
      txHash: record.txHash,
      status: record.status,
      confirmedAt: record.confirmedAt,
      createdAt: record.createdAt,
    });
  }
}
