package com.fp.bpm.save;

import java.util.List;

import com.fp.common.exception.CommonException;
import com.fp.common.exception.SavePointException;
import com.fp.common.helper.Constant;
import com.fp.common.logger.APPLogger;
import com.fp.dto.Response;
import com.fp.dto.rules.TransactionRule;
import com.fp.dto.save.SaveRequest;
import com.fp.persistence.commondb.PersistenceHelper;
import com.fp.persistence.commondb.db.DataHelper;
import com.fp.persistence.commondb.exception.CommondbException;
import com.fp.persistence.pgeneral.trans.TgeneTransactionProcess;

/**
 * Clase que se encarga del orquestamiento de reglas de negocio asociadas a una transaccion en modo normal.<br>
 * Proceso.<br>
 * <ul>
 * <li>Lee la definicion de las reglas definidas en la tabla tgenetransactionrule.</li>
 * <li>Recore las reglas.</li>
 * <li>Obtiene la clase especifica que implementa la interface TransactionRule.</li>
 * <li>Invoca a cada de una de las clases ejecutando el modo normal.</li>
 * </ul>
 * NOTA: Si se requere que grabe hay que invocar a la clase que se encarga de esta tarea.
 *
 * @author Jorge Vaca
 * @version 2.1
 */
public class SaveProcessor {

    /** Objeto con beans utilizados en el proceso de una transaccion. */
    private SaveRequest saveRequest;

    /**
     * Metodo que se encarga del procesar procesos de negocio en linea.
     *
     * @param pSaveRequest Objeto con beans utilizados en el proceso de una transaccion.
     * @param pResponse Objeto en el cual se alamcena la respuesta de una transaccion.
     * @return boolean
     * @throws CommondbException
     * @throws Exception
     */
    public boolean processReverse(SaveRequest pSaveRequest, Response pResponse) throws CommondbException, Exception {
        Thread.currentThread().setName(pSaveRequest.getJournalId());
        this.saveRequest = pSaveRequest;
        this.processrules(pResponse, false);
        return true;
    }

    /**
     * Metodo que se encarga del procesar procesos de negocio en linea.
     *
     * @param pSaveRequest Objeto con beans utilizados en el proceso de una transaccion.
     * @param pResponse Objeto en el cual se alamcena la respuesta de una transaccion.
     * @return boolean
     * @throws CommondbException
     * @throws Exception
     */
    public boolean process(SaveRequest pSaveRequest, Response pResponse) throws CommondbException, Exception {
        Thread.currentThread().setName(pSaveRequest.getJournalId());
        this.saveRequest = pSaveRequest;
        this.processrules(pResponse, true);
        return true;
    }

    /**
     * Metodo que se encarga del procesar procesos de negocio en linea.
     *
     * @param pSaveRequest Objeto con beans utilizados en el proceso de una transaccion.
     * @param pResponse Objeto en el cual se alamcena la respuesta de una transaccion.
     * @return boolean
     * @throws CommondbException
     * @throws Exception
     */
    public boolean processWithoutSave(SaveRequest pSaveRequest, Response pResponse) throws CommondbException, Exception {
        try {
            this.saveRequest = pSaveRequest;
            this.processrules(pResponse, false);
        } catch (Exception e) {
            throw e;
        }
        return true;
    }

    /**
     * Metodo que se encarga del procesar procesos de negocios que se invocan desde un proceso de fin de dia..
     *
     * @param pSaveRequest Objeto con beans utilizados en el proceso de una transaccion.
     * @param pResponse Objeto en el cual se alamcena la respuesta de una transaccion.
     * @return boolean
     * @throws CommondbException
     * @throws Exception
     */
    public boolean processBatch(SaveRequest pSaveRequest, Response pResponse) throws CommondbException, Exception {
        try {
            this.saveRequest = pSaveRequest;
            this.processrules(pResponse, true);
        } catch (Exception e) {
            throw e;
        }
        return true;
    }

    /**
     * Metodo que se encarga del procesar procesos de negocio.
     *
     * @param pResponse Objeto en el cual se alamcena la respuesta de una transaccion.
     * @param pCommit Indica si se hace commit o no.
     * @return boolean
     * @throws CommondbException
     * @throws Exception
     */
    private void processrules(Response pResponse, boolean pCommit) throws CommondbException, Exception {
        List<TgeneTransactionProcess> lObjects = this.getProcess();
        if (lObjects != null) {
            for (TgeneTransactionProcess obj : lObjects) {
                try {
                    this.processByRule(obj);
                } catch (SavePointException e) {
                    // Detiene la ejecucion de componentes de negocio y graba la transaccion.
                    break;
                }

            }
        }
        // Ejecuta dml contra la base
        // ojo no hacer commit por que esto se usa en el batch
        if (pCommit) {
            Save save = new Save();
            save.process(this.saveRequest, pResponse);
        }
    }

    /**
     * Metodo encargado de ejecutar cada una de las reglas definidas en la transaccion.
     *
     * @param pProcess Objeto que contiene la regla a ejecutar.
     * @throws Exception
     */
    private void processByRule(TgeneTransactionProcess pProcess) throws Exception {
        TransactionRule rule = null;
        try {
            rule = (TransactionRule) Class.forName(pProcess.getProcesscode()).newInstance();
        } catch (ClassNotFoundException e) {
            throw new CommonException("COMMON-0006", "REGLA {0} A EJECUTAR NO DEFINIDO MODULE: {1} TRANS: {2} VERSION {3}",
                    pProcess.getProcesscode(), e, this.saveRequest.getTransactionModule(), this.saveRequest.getTransactionCode(),
                    this.saveRequest.getTransactionVersion());
        } catch (ClassCastException e) {
            throw new CommonException("COMMON-0007", "REGLA {0} A EJECUTAR MODULE: {1} TRANS: {2} VERSION {3} NO IMPLEMENTA {4}", e,
                    pProcess.getProcesscode(), this.saveRequest.getTransactionModule(), this.saveRequest.getTransactionCode(),
                    this.saveRequest.getTransactionVersion(), TransactionRule.class.getName());
        }
        APPLogger.getLogger().info(rule);
        String reverse = (String) this.saveRequest.get("reverse");
        rule.setFlow(pProcess.getProcessname());
        rule.setRule(pProcess.getRulename());
        if (Constant.ifYes(reverse)) {
            rule.reverseProcess(this.saveRequest);
        } else {
            rule.normalProcess(this.saveRequest);
        }

    }

    /**
     * Metodo que obtiene una lista de reglas definidos a nivel de transaccion.
     *
     * @return List<TgeneTransactionProcess>
     * @throws Exception
     */
    private List<TgeneTransactionProcess> getProcess() throws Exception {
        List<TgeneTransactionProcess> lObjects = DataHelper.getInstance().getTgeneTransactionProcess(this.saveRequest.getTransactionModule(),
                this.saveRequest.getTransactionCode(), this.saveRequest.getTransactionVersion());

        /*
         * No va porque existe transacciones normales if(lObjects == null || lObjects.isEmpty()){ throw new
         * CommonException("COMMON-0006",
         * "REGLAS NO DEFINIDAS RN TGENETRANSACTIONPROCESS MODULE: {1} TRANS: {2} VERSION {3}",
         * saveRequest.getTransactionModule(),saveRequest.getTransactionCode(), saveRequest.getTransactionVersion()); }
         */
        return lObjects;
    }

    /**
     * Envia los beans a la base de datos y limpia los bean del save que ya se enviaron a al abse.
     *
     * @param saverequest
     * @throws Exception
     */
    public static void saveAndFlush(SaveRequest saverequest) throws Exception {
        Save save = new Save();
        save.process(saverequest, saverequest.getResponse());
        PersistenceHelper.flushTransaction();
        Save.cleanSaveBeans(saverequest);
    }
}
