package org.jpos.tcpay.service;

import org.jpos.iso.ISOMsg;
import org.jpos.tcpay.db.entity.*;
import org.jpos.util.AppLogger;

/**
 * Service class for managing the transaction lifecycle
 * Handles the flow: temp -> success/failed transactions
 */
public class TransactionLifecycleService implements TransactionLifeCycle {
    
    private final TransactionAuxUtils transactionAuxUtils;
    private final AppLogger logger = new AppLogger();
    private final ReversalService reversalService;
    

    public TransactionLifecycleService(TransactionAuxUtils transactionAuxUtils, ReversalService reversalService) {
        this.transactionAuxUtils = transactionAuxUtils;
        this.reversalService = reversalService;
    }
    

    /**
     * Start a new transaction - creates temp transaction
     * @param request ISO request message
     * @param posTerminal POS terminal
     * @param acquirerTerminal Acquirer terminal
     * @return the created temp transaction
     * @throws RuntimeException if terminal is busy or creation fails
     */
    public PosTempTransaction startTransaction(ISOMsg request, PosTerminal posTerminal, AcquirerTerminal acquirerTerminal) {
        
        PosTempTransaction tempTxn = transactionAuxUtils.createTempTransactionFromISO(request, posTerminal, acquirerTerminal);
        return transactionAuxUtils.insertTempTransaction(tempTxn);
    }
    
    /**
     * Complete a successful transaction
     * @param tempTxn PosTempTransaction
     * @param response ISO response message
     */
    protected void completeSuccessTransaction(PosTempTransaction tempTxn, ISOMsg response) {
        try {
            if (tempTxn != null) {
                PosTransaction successTxn = transactionAuxUtils.createSuccessTransactionFromTemp(tempTxn, response);
                transactionAuxUtils.moveTempToSuccess(tempTxn, successTxn);
                logger.log("Successfully completed transaction ID: " + tempTxn);
            } else {
                logger.log("Warning: Could not find temp transaction with ID: " + tempTxn);
            }
        } catch (Exception e) {
            logger.log("Error completing success transaction: " + e.getMessage());
            throw new RuntimeException("Failed to complete success transaction", e);
        }
    }
    
    /**
     * Complete a failed transaction
     * @param tempTxn temp transaction
     * @param response ISO response message (can be null for timeouts)
     */
    protected void completeFailedTransaction(PosTempTransaction tempTxn, ISOMsg response) {
        try {
            // Find the temp transaction by ID to get the details needed
            if (tempTxn != null) {
                PosFailedTransaction failedTxn = transactionAuxUtils.createFailedTransactionFromTemp(tempTxn, response);
                transactionAuxUtils.moveTempToFailed(tempTxn, failedTxn);
                logger.log("Successfully completed failed transaction ID: " + tempTxn.getId());
            } else {
                logger.log("Warning: Could not find temp transaction with ID: " + tempTxn);
            }
        } catch (Exception e) {
            logger.log("Error completing failed transaction: " + e.getMessage());
            throw new RuntimeException("Failed to complete failed transaction", e);
        }
    }
    
    /**
     * Handle transaction completion based on response code
     * @param tempTxn ID of temp transaction
     * @param response ISO response message
     * @return true if successful, false if failed
     */
    @Override
    public boolean completeTransaction(PosTempTransaction tempTxn, ISOMsg response) {
        try {
            String responseCode = response != null ? response.getString(39) : null;
            
            // Check if it's a success response
            if (isSuccessResponse(responseCode)) {
                completeSuccessTransaction(tempTxn, response);
                return true;
            } else {
                completeFailedTransaction(tempTxn, response);
                return false;
            }
        } catch (Exception e) {
            logger.log("Error in transaction completion: " + e.getMessage());
            // Ensure failed transaction is recorded even if there's an error
            try {
                completeFailedTransaction(tempTxn, response);
            } catch (Exception ex) {
                logger.log("Critical error: Could not record failed transaction: " + ex.getMessage());
            }
            return false;
        }
    }
    
    /**
     * Clean up orphaned temp transactions (for error handling)
     * @param tempTxn ID of temp transaction to clean up
     * @param errorMessage Error message to record
     */
    public void cleanupTransaction(PosTempTransaction tempTxn, String errorMessage) {
        try {
            if (tempTxn != null) {
                PosFailedTransaction failedTxn = transactionAuxUtils.createFailedTransactionFromTemp(tempTxn, null);
                failedTxn.setResponseCode("96"); // System error
                failedTxn.setResponseMessage(errorMessage != null ? errorMessage : "System processing error");
                transactionAuxUtils.moveTempToFailed(tempTxn, failedTxn);
                logger.log("Cleaned up temp transaction ID: " + tempTxn.getId());
            }
        } catch (Exception e) {
            logger.log("Error during transaction cleanup: " + e.getMessage());
        }
    }

    @Override
    public void handleResponseTimeout(PosTempTransaction tempTransaction) {
        reversalService.handleResponseTimeout(tempTransaction);
    }

    @Override
    public void handleConnectionLoss(PosTempTransaction tempTransaction) {
        reversalService.handleConnectionLoss(tempTransaction);
    }

    @Override
    public void handleDatabaseError(PosTempTransaction tempTransaction, Exception dbError) {
        reversalService.handleDatabaseError(tempTransaction, dbError);
    }


    /**
     * Helper method to determine if response code indicates success
     */
    private boolean isSuccessResponse(String responseCode) {
        return "00".equals(responseCode) || "10".equals(responseCode) || "11".equals(responseCode);
    }
    
    /**
     * Helper method to find temp transaction by ID
     */
    private PosTempTransaction findTempTransactionById(Long id) {
        return transactionAuxUtils.getTempTransactionById(id);
    }
}