import { Injectable } from '@nestjs/common';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import { IFiatWithdrawalOrderRepository } from '@/domain/repositories';
import { FiatWithdrawalOrder } from '@/domain/aggregates';
import { FiatWithdrawalStatus } from '@/domain/value-objects/fiat-withdrawal-status.enum';
import Decimal from 'decimal.js';

@Injectable()
export class FiatWithdrawalOrderRepositoryImpl implements IFiatWithdrawalOrderRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(order: FiatWithdrawalOrder): Promise<FiatWithdrawalOrder> {
    const data = {
      orderNo: order.orderNo,
      accountSequence: order.accountSequence,
      userId: order.userId.value,
      amount: order.amount.toDecimal(),
      fee: order.fee.toDecimal(),
      paymentMethod: order.paymentMethod,
      bankName: order.bankName,
      bankCardNo: order.bankCardNo,
      cardHolderName: order.cardHolderName,
      alipayAccount: order.alipayAccount,
      alipayRealName: order.alipayRealName,
      wechatAccount: order.wechatAccount,
      wechatRealName: order.wechatRealName,
      status: order.status,
      errorMessage: order.errorMessage,
      reviewedBy: order.reviewedBy,
      reviewedAt: order.reviewedAt,
      reviewRemark: order.reviewRemark,
      paidBy: order.paidBy,
      paidAt: order.paidAt,
      paymentProof: order.paymentProof,
      paymentRemark: order.paymentRemark,
      detailMemo: order.detailMemo,
      frozenAt: order.frozenAt,
      completedAt: order.completedAt,
    };

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

  async findByOrderNo(orderNo: string): Promise<FiatWithdrawalOrder | null> {
    const record = await this.prisma.fiatWithdrawalOrder.findUnique({
      where: { orderNo },
    });
    return record ? this.toDomain(record) : null;
  }

  async findByUserId(userId: bigint): Promise<FiatWithdrawalOrder[]> {
    const records = await this.prisma.fiatWithdrawalOrder.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
    });
    return records.map(r => this.toDomain(r));
  }

  async findByAccountSequence(accountSequence: string): Promise<FiatWithdrawalOrder[]> {
    const records = await this.prisma.fiatWithdrawalOrder.findMany({
      where: { accountSequence },
      orderBy: { createdAt: 'desc' },
    });
    return records.map(r => this.toDomain(r));
  }

  async findByStatus(status: FiatWithdrawalStatus): Promise<FiatWithdrawalOrder[]> {
    const records = await this.prisma.fiatWithdrawalOrder.findMany({
      where: { status },
      orderBy: { createdAt: 'asc' },
    });
    return records.map(r => this.toDomain(r));
  }

  async findByStatuses(statuses: FiatWithdrawalStatus[]): Promise<FiatWithdrawalOrder[]> {
    const records = await this.prisma.fiatWithdrawalOrder.findMany({
      where: { status: { in: statuses } },
      orderBy: { createdAt: 'asc' },
    });
    return records.map(r => this.toDomain(r));
  }

  async findPendingReview(): Promise<FiatWithdrawalOrder[]> {
    const records = await this.prisma.fiatWithdrawalOrder.findMany({
      where: {
        status: {
          in: [FiatWithdrawalStatus.FROZEN, FiatWithdrawalStatus.REVIEWING],
        },
      },
      orderBy: { createdAt: 'asc' },
    });
    return records.map(r => this.toDomain(r));
  }

  async findPendingPayment(): Promise<FiatWithdrawalOrder[]> {
    const records = await this.prisma.fiatWithdrawalOrder.findMany({
      where: {
        status: {
          in: [FiatWithdrawalStatus.APPROVED, FiatWithdrawalStatus.PAYING],
        },
      },
      orderBy: { createdAt: 'asc' },
    });
    return records.map(r => this.toDomain(r));
  }

  private toDomain(record: {
    id: bigint;
    orderNo: string;
    accountSequence: string;
    userId: bigint;
    amount: Decimal;
    fee: Decimal;
    paymentMethod: string;
    bankName: string | null;
    bankCardNo: string | null;
    cardHolderName: string | null;
    alipayAccount: string | null;
    alipayRealName: string | null;
    wechatAccount: string | null;
    wechatRealName: string | null;
    status: string;
    errorMessage: string | null;
    reviewedBy: string | null;
    reviewedAt: Date | null;
    reviewRemark: string | null;
    paidBy: string | null;
    paidAt: Date | null;
    paymentProof: string | null;
    paymentRemark: string | null;
    detailMemo: string | null;
    frozenAt: Date | null;
    completedAt: Date | null;
    createdAt: Date;
  }): FiatWithdrawalOrder {
    return FiatWithdrawalOrder.reconstruct({
      id: record.id,
      orderNo: record.orderNo,
      accountSequence: record.accountSequence,
      userId: record.userId,
      amount: new Decimal(record.amount.toString()),
      fee: new Decimal(record.fee.toString()),
      paymentMethod: record.paymentMethod,
      bankName: record.bankName,
      bankCardNo: record.bankCardNo,
      cardHolderName: record.cardHolderName,
      alipayAccount: record.alipayAccount,
      alipayRealName: record.alipayRealName,
      wechatAccount: record.wechatAccount,
      wechatRealName: record.wechatRealName,
      status: record.status,
      errorMessage: record.errorMessage,
      reviewedBy: record.reviewedBy,
      reviewedAt: record.reviewedAt,
      reviewRemark: record.reviewRemark,
      paidBy: record.paidBy,
      paidAt: record.paidAt,
      paymentProof: record.paymentProof,
      paymentRemark: record.paymentRemark,
      detailMemo: record.detailMemo,
      frozenAt: record.frozenAt,
      completedAt: record.completedAt,
      createdAt: record.createdAt,
    });
  }
}
