package org.jpos.tcpay.db.repository.impl;

import org.jpos.tcpay.db.entity.AcquirerTerminal;
import org.jpos.tcpay.db.entity.AcquirerTerminalBatch;
import org.jpos.tcpay.db.entity.AcquirerTerminalStan;
import org.jpos.tcpay.db.repository.AcquirerTerminalBatchRepository;
import org.jpos.tcpay.db.repository.BaseJpaRepository;

import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import java.time.LocalDate;
import java.util.Optional;

/**
 * JPA implementation of AcquirerTerminalBatchRepository
 */
public class AcquirerTerminalBatchRepositoryImpl extends BaseJpaRepository implements AcquirerTerminalBatchRepository {
    
    @Override
    public Optional<AcquirerTerminalBatch> findByAcquirerTerminalId(AcquirerTerminal acquirerTerminalId) {
        return executeWithEntityManager(em -> {
            try {
                TypedQuery<AcquirerTerminalBatch> query = em.createQuery(
                    "SELECT b FROM AcquirerTerminalBatch b WHERE b.acquirerTerminal = :acquirerTerminalId",
                    AcquirerTerminalBatch.class
                );
                query.setParameter("acquirerTerminalId", acquirerTerminalId);
                return Optional.of(query.getSingleResult());
            } catch (NoResultException e) {
                return Optional.empty();
            }
        });
    }
    
    @Override
    public AcquirerTerminalBatch save(AcquirerTerminalBatch acquirerTerminalBatch) {
        return executeInTransaction(em -> {
            if (acquirerTerminalBatch.getId() == null) {
                em.persist(acquirerTerminalBatch);
                return acquirerTerminalBatch;
            } else {
                return em.merge(acquirerTerminalBatch);
            }
        });
    }
    
    @Override
    public AcquirerTerminalBatch findOrCreate(AcquirerTerminal acquirerTerminalId) {
        Optional<AcquirerTerminalBatch> existing = findByAcquirerTerminalId(acquirerTerminalId);
        
        if (existing.isPresent()) {
            return existing.get();
        }
        
        // Create new record
        AcquirerTerminalBatch newBatch = new AcquirerTerminalBatch()
                .setAcquirerTerminal(acquirerTerminalId)
                .setCurrentBatch(1)
                .setMaxBatch(999999)
                .setLastResetDate(LocalDate.now());
        
        return save(newBatch);
    }
    
    @Override
    public Integer getAndIncrementBatch(AcquirerTerminal acquirerTerminalId) {
        return executeInTransaction(em -> {
            // Ensure record exists
            AcquirerTerminalBatch batch = findOrCreate(acquirerTerminalId);
            
            // Use JPA query for atomic increment
            int updatedRows = em.createQuery(
                "UPDATE AcquirerTerminalBatch b SET b.currentBatch = b.currentBatch + 1 " +
                "WHERE b.acquirerTerminal = :acquirerTerminalId"
            )
            .setParameter("acquirerTerminalId", acquirerTerminalId)
            .executeUpdate();
            
            if (updatedRows == 0) {
                logger.log("Failed to increment Batch for acquirer terminal " + acquirerTerminalId.getTerminalId());
                return 1; // Fallback
            }
            
            // Get the incremented value
            TypedQuery<Integer> selectQuery = em.createQuery(
                "SELECT b.currentBatch FROM AcquirerTerminalBatch b WHERE b.acquirerTerminal = :acquirerTerminalId",
                Integer.class
            );
            selectQuery.setParameter("acquirerTerminalId", acquirerTerminalId);
            
            return selectQuery.getSingleResult();
        });
    }
    
    @Override
    public void resetBatchIfNeeded(AcquirerTerminal acquirerTerminalId, LocalDate currentDate) {
        AcquirerTerminalBatch acquirerTerminalBatch = findOrCreate(acquirerTerminalId);
        if (acquirerTerminalBatch.getCurrentBatch() >= acquirerTerminalBatch.getMaxBatch()) {
            logger.log("Batch exceeded max for acquirer terminal " + acquirerTerminalId.getTerminalId() + ", resetting to 1");
            save(acquirerTerminalBatch.setCurrentBatch(1).setLastResetDate(currentDate));
        }

    }
}