import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { TransactionRequestMapper } from '../mappers/transaction-request.mapper';
import { ITransactionRequestRepository } from '@/domain/repositories/transaction-request.repository.interface';
import { TransactionRequest } from '@/domain/aggregates/transaction-request';
import { ChainType, TxHash } from '@/domain/value-objects';
import { TransactionStatus } from '@/domain/enums';

@Injectable()
export class TransactionRequestRepositoryImpl implements ITransactionRequestRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(request: TransactionRequest): Promise<TransactionRequest> {
    const data = TransactionRequestMapper.toPersistence(request);

    if (request.id) {
      const updated = await this.prisma.transactionRequest.update({
        where: { id: request.id },
        data,
      });
      return TransactionRequestMapper.toDomain(updated);
    } else {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { id: _, ...createData } = data;
      const created = await this.prisma.transactionRequest.create({
        data: createData,
      });
      return TransactionRequestMapper.toDomain(created);
    }
  }

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

  async findByTxHash(txHash: TxHash): Promise<TransactionRequest | null> {
    const record = await this.prisma.transactionRequest.findFirst({
      where: { txHash: txHash.toString() },
    });
    return record ? TransactionRequestMapper.toDomain(record) : null;
  }

  async findBySource(
    sourceService: string,
    sourceOrderId: string,
  ): Promise<TransactionRequest | null> {
    const record = await this.prisma.transactionRequest.findUnique({
      where: {
        uk_source_order: {
          sourceService,
          sourceOrderId,
        },
      },
    });
    return record ? TransactionRequestMapper.toDomain(record) : null;
  }

  async findPending(chainType: ChainType): Promise<TransactionRequest[]> {
    const records = await this.prisma.transactionRequest.findMany({
      where: {
        chainType: chainType.toString(),
        status: TransactionStatus.PENDING,
      },
      orderBy: { createdAt: 'asc' },
    });
    return records.map(TransactionRequestMapper.toDomain);
  }

  async findBroadcasted(chainType: ChainType): Promise<TransactionRequest[]> {
    const records = await this.prisma.transactionRequest.findMany({
      where: {
        chainType: chainType.toString(),
        status: TransactionStatus.BROADCASTED,
      },
      orderBy: { createdAt: 'asc' },
    });
    return records.map(TransactionRequestMapper.toDomain);
  }

  async findRetryable(chainType: ChainType, maxRetries: number): Promise<TransactionRequest[]> {
    const records = await this.prisma.transactionRequest.findMany({
      where: {
        chainType: chainType.toString(),
        status: TransactionStatus.FAILED,
        retryCount: { lt: maxRetries },
      },
      orderBy: { createdAt: 'asc' },
    });
    return records.map(TransactionRequestMapper.toDomain);
  }
}
