import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import {
  IMarketMakerDepositRepository,
  MarketMakerDepositDto,
} from '@/domain/repositories/market-maker-deposit.repository.interface';
import { ChainType, BlockNumber } from '@/domain/value-objects';
import { MarketMakerAssetType, MarketMakerDepositStatus } from '@/domain/enums';

@Injectable()
export class MarketMakerDepositRepositoryImpl implements IMarketMakerDepositRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(deposit: MarketMakerDepositDto): Promise<MarketMakerDepositDto> {
    const data = {
      chainType: deposit.chainType,
      txHash: deposit.txHash,
      fromAddress: deposit.fromAddress,
      toAddress: deposit.toAddress,
      assetType: deposit.assetType,
      tokenContract: deposit.tokenContract,
      amount: new Prisma.Decimal(deposit.amount.toString()),
      amountFormatted: new Prisma.Decimal(deposit.amountFormatted),
      blockNumber: deposit.blockNumber,
      blockTimestamp: deposit.blockTimestamp,
      logIndex: deposit.logIndex,
      confirmations: deposit.confirmations,
      status: deposit.status,
      notifiedAt: deposit.notifiedAt,
      notifyAttempts: deposit.notifyAttempts,
      lastNotifyError: deposit.lastNotifyError,
      creditedAt: deposit.creditedAt,
      creditReference: deposit.creditReference,
    };

    if (deposit.id) {
      const updated = await this.prisma.marketMakerDeposit.update({
        where: { id: deposit.id },
        data,
      });
      return this.mapToDto(updated);
    } else {
      const created = await this.prisma.marketMakerDeposit.create({
        data,
      });
      return this.mapToDto(created);
    }
  }

  async findById(id: bigint): Promise<MarketMakerDepositDto | null> {
    const record = await this.prisma.marketMakerDeposit.findUnique({
      where: { id },
    });
    return record ? this.mapToDto(record) : null;
  }

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

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

  async findPendingConfirmation(chainType: ChainType): Promise<MarketMakerDepositDto[]> {
    const records = await this.prisma.marketMakerDeposit.findMany({
      where: {
        chainType: chainType.toString(),
        status: {
          in: [MarketMakerDepositStatus.DETECTED, MarketMakerDepositStatus.CONFIRMING],
        },
      },
      orderBy: { blockNumber: 'asc' },
    });
    return records.map(this.mapToDto);
  }

  async findPendingNotification(): Promise<MarketMakerDepositDto[]> {
    const records = await this.prisma.marketMakerDeposit.findMany({
      where: {
        status: MarketMakerDepositStatus.CONFIRMED,
        notifiedAt: null,
      },
      orderBy: { createdAt: 'asc' },
      take: 100,
    });
    return records.map(this.mapToDto);
  }

  async findByAssetType(
    chainType: ChainType,
    assetType: MarketMakerAssetType,
    limit: number = 50,
  ): Promise<MarketMakerDepositDto[]> {
    const records = await this.prisma.marketMakerDeposit.findMany({
      where: {
        chainType: chainType.toString(),
        assetType,
      },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
    return records.map(this.mapToDto);
  }

  async updateConfirmations(
    id: bigint,
    confirmations: number,
    status: MarketMakerDepositStatus,
  ): Promise<void> {
    await this.prisma.marketMakerDeposit.update({
      where: { id },
      data: { confirmations, status },
    });
  }

  async updateNotifyStatus(
    id: bigint,
    notifiedAt: Date,
    notifyAttempts: number,
    error?: string,
  ): Promise<void> {
    await this.prisma.marketMakerDeposit.update({
      where: { id },
      data: {
        notifiedAt,
        notifyAttempts,
        lastNotifyError: error,
      },
    });
  }

  async updateCreditedStatus(
    id: bigint,
    creditedAt: Date,
    creditReference: string,
  ): Promise<void> {
    await this.prisma.marketMakerDeposit.update({
      where: { id },
      data: {
        creditedAt,
        creditReference,
        status: MarketMakerDepositStatus.CREDITED,
      },
    });
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  private mapToDto(record: any): MarketMakerDepositDto {
    return {
      id: record.id,
      chainType: record.chainType,
      txHash: record.txHash,
      fromAddress: record.fromAddress,
      toAddress: record.toAddress,
      assetType: record.assetType as MarketMakerAssetType,
      tokenContract: record.tokenContract,
      amount: BigInt(record.amount.toString()),
      amountFormatted: record.amountFormatted.toString(),
      blockNumber: record.blockNumber,
      blockTimestamp: record.blockTimestamp,
      logIndex: record.logIndex,
      confirmations: record.confirmations,
      status: record.status as MarketMakerDepositStatus,
      notifiedAt: record.notifiedAt,
      notifyAttempts: record.notifyAttempts,
      lastNotifyError: record.lastNotifyError,
      creditedAt: record.creditedAt,
      creditReference: record.creditReference,
      createdAt: record.createdAt,
      updatedAt: record.updatedAt,
    };
  }
}
