package org.jpos.tcpay.db.repository;

import org.jpos.tcpay.db.entity.PosTempTransaction;
import org.jpos.util.AppLogger;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * JPA implementation of PosTempTransactionRepository
 * Uses EntityManager for all database operations
 */
public class PosTempTransactionRepositoryImpl implements PosTempTransactionRepository {
    
    private final EntityManagerFactory emf;
    private final AppLogger logger = new AppLogger();
    
    public PosTempTransactionRepositoryImpl(EntityManagerFactory emf) {
        this.emf = emf;
    }
    
    @Override
    public PosTempTransaction save(PosTempTransaction tempTransaction) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        
        try {
            tx.begin();
            
            if (tempTransaction.getId() == null) {
                // New entity - persist
                em.persist(tempTransaction);
            } else {
                // Existing entity - merge
                tempTransaction = em.merge(tempTransaction);
            }
            
            tx.commit();
            logger.log("Saved temp transaction: " + tempTransaction.getId());
            return tempTransaction;
            
        } catch (Exception e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            logger.log("Error saving temp transaction: " + e.getMessage());
            throw new RuntimeException("Failed to save temp transaction", e);
        } finally {
            em.close();
        }
    }
    
    @Override
    public Optional<PosTempTransaction> findById(Long id) {
        EntityManager em = emf.createEntityManager();
        try {
            PosTempTransaction transaction = em.find(PosTempTransaction.class, id);
            return Optional.ofNullable(transaction);
        } catch (Exception e) {
            logger.log("Error finding temp transaction by ID: " + e.getMessage());
            return Optional.empty();
        } finally {
            em.close();
        }
    }
    
    @Override
    public Optional<PosTempTransaction> findByBankTidAndBankMid(String bTid, String bMid) {
        EntityManager em = emf.createEntityManager();
        try {
            TypedQuery<PosTempTransaction> query = em.createQuery(
                "SELECT t FROM PosTempTransaction t WHERE t.bTid = :bTid AND t.bMid = :bMid", 
                PosTempTransaction.class
            );
            query.setParameter("bTid", bTid);
            query.setParameter("bMid", bMid);
            
            List<PosTempTransaction> results = query.getResultList();
            return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));
            
        } catch (Exception e) {
            logger.log("Error finding temp transaction by bank IDs: " + e.getMessage());
            return Optional.empty();
        } finally {
            em.close();
        }
    }
    
    @Override
    public boolean existsByBankTidAndBankMidAndAcquirerId(String bTid, String bMid, Long acquirerId) {
        EntityManager em = emf.createEntityManager();
        try {
            TypedQuery<Long> query = em.createQuery(
                "SELECT COUNT(t) FROM PosTempTransaction t WHERE t.bTid = :bTid AND t.bMid = :bMid AND t.acquirerId = :acquirerId",
                Long.class
            );
            query.setParameter("bTid", bTid);
            query.setParameter("bMid", bMid);
            query.setParameter("acquirerId", acquirerId);
            
            Long count = query.getSingleResult();
            return count > 0;
            
        } catch (Exception e) {
            logger.log("Error checking temp transaction existence: " + e.getMessage());
            return false;
        } finally {
            em.close();
        }
    }
    
    @Override
    public List<PosTempTransaction> findStaleTransactions(LocalDateTime cutoffTime) {
        EntityManager em = emf.createEntityManager();
        try {
            TypedQuery<PosTempTransaction> query = em.createQuery(
                "SELECT t FROM PosTempTransaction t WHERE t.createdDateTime < :cutoffTime", 
                PosTempTransaction.class
            );
            query.setParameter("cutoffTime", cutoffTime);
            
            return query.getResultList();
            
        } catch (Exception e) {
            logger.log("Error finding stale temp transactions: " + e.getMessage());
            return List.of();
        } finally {
            em.close();
        }
    }
    
    @Override
    public long count() {
        EntityManager em = emf.createEntityManager();
        try {
            TypedQuery<Long> query = em.createQuery(
                "SELECT COUNT(t) FROM PosTempTransaction t", 
                Long.class
            );
            return query.getSingleResult();
            
        } catch (Exception e) {
            logger.log("Error counting temp transactions: " + e.getMessage());
            return 0;
        } finally {
            em.close();
        }
    }
    
    @Override
    public long countStaleTransactions(LocalDateTime cutoffTime) {
        EntityManager em = emf.createEntityManager();
        try {
            TypedQuery<Long> query = em.createQuery(
                "SELECT COUNT(t) FROM PosTempTransaction t WHERE t.createdDateTime < :cutoffTime", 
                Long.class
            );
            query.setParameter("cutoffTime", cutoffTime);
            
            return query.getSingleResult();
            
        } catch (Exception e) {
            logger.log("Error counting stale temp transactions: " + e.getMessage());
            return 0;
        } finally {
            em.close();
        }
    }
    
    @Override
    public void deleteById(Long id) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        
        try {
            tx.begin();
            
            PosTempTransaction transaction = em.find(PosTempTransaction.class, id);
            if (transaction != null) {
                em.remove(transaction);
                logger.log("Deleted temp transaction: " + id);
            } else {
                logger.log("Warning: Temp transaction not found for deletion: " + id);
            }
            
            tx.commit();
            
        } catch (Exception e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            logger.log("Error deleting temp transaction: " + e.getMessage());
            throw new RuntimeException("Failed to delete temp transaction", e);
        } finally {
            em.close();
        }
    }

    @Override
    public void delete(PosTempTransaction tempTransaction) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        try {
            tx.begin();

            if (tempTransaction != null && tempTransaction.getId() != null) {
                // Fetch the entity fresh from the database to ensure it's attached
                PosTempTransaction managedEntity = em.find(PosTempTransaction.class, tempTransaction.getId());
                
                if (managedEntity != null) {
                    em.remove(managedEntity);
                    logger.log("Deleted temp transaction: " + tempTransaction.getId());
                } else {
                    logger.log("Warning: Temp transaction not found in database for deletion: " + tempTransaction.getId());
                }
            } else {
                logger.log("Warning: Invalid temp transaction for deletion (null or no ID): " + tempTransaction);
            }

            tx.commit();

        } catch (Exception e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            logger.log("Error deleting temp transaction: " + e.getMessage());
            throw new RuntimeException("Failed to delete temp transaction", e);
        } finally {
            em.close();
        }
    }


    @Override
    public void updateStatus(Long id, PosTempTransaction.TempTransactionStatus status) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        
        try {
            tx.begin();
            
            PosTempTransaction transaction = em.find(PosTempTransaction.class, id);
            if (transaction != null) {
                transaction.setStatus(status);
                em.merge(transaction);
                logger.log("Updated temp transaction status: " + id + " -> " + status);
            } else {
                logger.log("Warning: Temp transaction not found for status update: " + id);
            }
            
            tx.commit();
            
        } catch (Exception e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            logger.log("Error updating temp transaction status: " + e.getMessage());
            throw new RuntimeException("Failed to update temp transaction status", e);
        } finally {
            em.close();
        }
    }
}