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

import org.jpos.tcpay.db.entity.AcquirerTerminal;
import org.jpos.tcpay.db.entity.AcquirerTerminalStan;
import org.jpos.tcpay.db.repository.AcquirerTerminalStanRepository;
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 AcquirerTerminalStanRepository
 */
public class AcquirerTerminalStanRepositoryImpl extends BaseJpaRepository implements AcquirerTerminalStanRepository {
    
    @Override
    public Optional<AcquirerTerminalStan> find(final AcquirerTerminal acquirerTerminalId) {
        return executeWithEntityManager(em -> {
            try {
                TypedQuery<AcquirerTerminalStan> query = em.createQuery(
                    "SELECT s FROM AcquirerTerminalStan s WHERE s.acquirerTerminal = :acquirerTerminalId",
                    AcquirerTerminalStan.class
                );
                query.setParameter("acquirerTerminalId", acquirerTerminalId);
                return Optional.of(query.getSingleResult());
            } catch (NoResultException e) {
                return Optional.empty();
            }
        });
    }
    
    @Override
    public AcquirerTerminalStan save(AcquirerTerminalStan acquirerTerminalStan) {
        return executeInTransaction(em -> {
            if (acquirerTerminalStan.getId() == null) {
                em.persist(acquirerTerminalStan);
                return acquirerTerminalStan;
            } else {
                return em.merge(acquirerTerminalStan);
            }
        });
    }
    
    @Override
    public AcquirerTerminalStan findOrCreate(final AcquirerTerminal acquirerTerminalId) {
        Optional<AcquirerTerminalStan> existing = find(acquirerTerminalId);
        
        if (existing.isPresent()) {
            return existing.get();
        }
        
        // Create new record
        AcquirerTerminalStan newStan = new AcquirerTerminalStan()
                .setCurrentStan(1)
                .setMaxStan(999999)
                .setAcquirerTerminal(acquirerTerminalId)
                .setLastResetDate(LocalDate.now());
        
        return save(newStan);
    }
    
    @Override
    public Integer getAndIncrementStan(final AcquirerTerminal acquirerTerminalId) {
        return executeInTransaction(em -> {
            // Ensure record exists
            AcquirerTerminalStan stan = findOrCreate(acquirerTerminalId);
            
            // Use JPA query for atomic increment
            int updatedRows = em.createQuery(
                "UPDATE AcquirerTerminalStan s SET s.currentStan = s.currentStan + 1 " +
                "WHERE s.acquirerTerminal = :acquirerTerminalId"
            )
            .setParameter("acquirerTerminalId", acquirerTerminalId)
            .executeUpdate();
            
            if (updatedRows == 0) {
                logger.log("Failed to increment Stan for acquirer terminal " + acquirerTerminalId.getTerminalId());
                return 1; // Fallback
            }
            
            // Get the incremented value
            TypedQuery<Integer> selectQuery = em.createQuery(
                "SELECT s.currentStan FROM AcquirerTerminalStan s WHERE s.acquirerTerminal = :acquirerTerminalId",
                Integer.class
            );
            selectQuery.setParameter("acquirerTerminalId", acquirerTerminalId);
            
            return selectQuery.getSingleResult();
        });
    }
    
    @Override
    public boolean resetStanIfNeeded(final AcquirerTerminal acquirerTerminalId, LocalDate currentDate) {
        AcquirerTerminalStan acquirerTerminalStan = findOrCreate(acquirerTerminalId);
        if (acquirerTerminalStan.getCurrentStan() >= acquirerTerminalStan.getMaxStan()) {
            logger.log("Stan exceeded max for acquirer terminal " + acquirerTerminalId.getTerminalId() + ", resetting to 1");
            save(acquirerTerminalStan.setCurrentStan(1).setLastResetDate(currentDate));
            return true;
        }

        return false;

    }
}