import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import {
  IPoolAccountDepositRepository,
  PoolAccountDepositDto,
} from '@/domain/repositories/pool-account-deposit.repository.interface';
import { ChainType } from '@/domain/value-objects';
import { PoolAccountType, PoolAccountDepositStatus } from '@/domain/enums';

@Injectable()
export class PoolAccountDepositRepositoryImpl implements IPoolAccountDepositRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(deposit: PoolAccountDepositDto): Promise<PoolAccountDepositDto> {
    const data = {
      chainType: deposit.chainType,
      txHash: deposit.txHash,
      fromAddress: deposit.fromAddress,
      toAddress: deposit.toAddress,
      poolType: deposit.poolType,
      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,
      creditedAt: deposit.creditedAt,
      creditReference: deposit.creditReference,
    };

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

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

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

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

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

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

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  private mapToDto(record: any): PoolAccountDepositDto {
    return {
      id: record.id,
      chainType: record.chainType,
      txHash: record.txHash,
      fromAddress: record.fromAddress,
      toAddress: record.toAddress,
      poolType: record.poolType as PoolAccountType,
      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 PoolAccountDepositStatus,
      creditedAt: record.creditedAt,
      creditReference: record.creditReference,
      createdAt: record.createdAt,
      updatedAt: record.updatedAt,
    };
  }
}
