package com.fitbank.bpmserver;

import java.sql.Clob;
import java.sql.Date;
import java.util.List;
import java.util.Set;

import org.hibernate.LockMode;
import org.hibernate.SQLQuery;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.ProcessInstanceQuery;

import com.fitbank.bpm.client.BPMProcessor;
import com.fitbank.bpm.client.BPMProperties;
import com.fitbank.common.ApplicationDates;
import com.fitbank.common.BeanManager;
import com.fitbank.common.Helper;
import com.fitbank.common.conectivity.HbSession;
import com.fitbank.common.hb.UtilHB;
import com.fitbank.dto.management.Detail;
import com.fitbank.hb.persistence.gene.Tflowinstance;
import com.fitbank.hb.persistence.gene.Tflowinstancevariable;
import com.fitbank.hb.persistence.gene.TflowinstancevariableKey;

public class EndFlows extends ServiceBpm {

    public EndFlows() {
        this.setName("EndFlows");
    }

    private static final String SQL_FLUJOAUTORIZACION = "update tautorizacionflujo set estatus='I' where nombreinstancia=:instancia and estatus is null";

    @Override
    public Integer getPeriod() {
        return BPMProperties.getConfig().getInt("fit.bpm.endflows.period");
    }

    private void manageFlows() {
        BPMProcessor cli = new BPMProcessor();
        ProcessInstanceQuery qry = cli.getExecutionService().createProcessInstanceQuery();
        List<ProcessInstance> lpi = qry.list();
        for (ProcessInstance pi : lpi) {
            LOGGER.debug("Revisando flujo " + pi.getId() + "...");
            BPMProcessor c = BPMProcessor.findProcessInstanceById(pi.getId());
            if (c == null) {
                LOGGER.warn("Flujo " + pi.getId() + " ya no existe en la base de conocimientos");
                continue;
            }

            Set<String> varNames = c.getVariableNames();
            Detail detail = (Detail) c.getVariable("detail");
            if (detail == null) {
                continue;
            }

            if (!pi.isEnded() && (detail.findFieldByNameCreate("_BPMEND")!= null 
                    && detail.findFieldByNameCreate("_BPMEND").getValue() != null
                    && detail.findFieldByNameCreate("_BPMEND").getStringValue()
                   .compareTo("1") != 0)) {
                continue;
            }
            if (pi.getId().indexOf("comAutorization.") == 0) {
                continue;
            }
            if (!varNames.contains("detail")) {
                continue;
            }
            Helper.beginTransaction();
            try {
                this.manageFlow(c);
                Thread.sleep(100);
                Helper.commitTransaction();
            } catch (Exception e) {
                LOGGER.error("Excepcion al procesar el flujo, haciendo rollback", e);
                try {
                    Helper.rollbackTransaction();
                } catch (Exception e1) {
                    LOGGER.error("No se pudo hacer rollback", e1);
                }
                continue;
            }
        }
    }

    private Tflowinstance findInstance(String pid) {
        UtilHB util = new UtilHB("from com.fitbank.hb.persistence.gene.Tflowinstance o where o.nombreinstancia=:pid"
                + " and o.finalizado=:ended",
                LockMode.UPGRADE_NOWAIT, "o");
        util.setString("pid", pid);
        util.setString("ended", "0");
        return (Tflowinstance) util.getObject();
    }

    private void manageFlow(BPMProcessor c) throws Exception {
        boolean subflujo = false;
        Thread.sleep(100);
        String pid = c.getPid();
        Detail detail = (Detail) c.getVariable("detail");
        Tflowinstance ins = this.findInstance(pid);
        if (ins == null) {
            subflujo = true;
        }
        boolean ended = false;
        if ((detail.findFieldByNameCreate("_BPMEND").getValue() != null)
                && (detail.findFieldByNameCreate("_BPMEND").getStringValue().compareTo("1") == 0)) {
            ended = true;
        }
        if (!subflujo) {
            if (ended) {
                ins.setFinalizado("1");
                SQLQuery util = Helper.createSQLQuery(EndFlows.SQL_FLUJOAUTORIZACION);
                util.setString("instancia", ins.getNombreinstancia());
                util.executeUpdate();
            } else {
                ins.setFinalizado("0");
            }
            List<String> states = c.findActualStates();
            boolean first = true;
            String stateData = "";
            for (String state : states) {
                if (first) {
                    first = false;
                    state = "|" + state;
                }
                stateData += state;
            }
            ins.setActividadactual(stateData);
            Helper.saveOrUpdate(ins);
        }
        if (ended && ins != null) {
            this.saveVariables(pid, ins.getPk(), c);
            c.end();
        }
        else {
            c.end();
        }

    }
    private void saveVariables(String pPid, String pMessageId, BPMProcessor pClient) throws Exception {
        Set<String> varNames = pClient.getVariableNames();
        Date date = ApplicationDates.getDBDate();
        for (String varName : varNames) {
            LOGGER.debug("Flujo: " + pPid + "; Variable: " + varName);
            Object val = pClient.getVariable(varName);
            String sVal;
            if (val instanceof Detail) {
                sVal = ((Detail) val).toErrorXml();
            } else {
                sVal = BeanManager.convertObject(val, String.class);
            }
            if (sVal == null) {
                continue;
            }
            TflowinstancevariableKey varK = new TflowinstancevariableKey(pMessageId, varName);
            Clob cValue = BeanManager.convertObject(sVal, Clob.class);
            Tflowinstancevariable var = new Tflowinstancevariable(varK, cValue, pPid);
            var.setFfinalizado(date);
            Helper.saveOrUpdate(var);

        }
    }

    @Override
    public void execute() {
        if (!BPMProperties.getConfig().getBoolean("fit.bpm.process")) {
            return;
        }

        LOGGER.debug("Ejecutando Dada de Baja de Flujos en BPM cada " + this.getPeriod() + " minutos");
        Helper.setSession(HbSession.getInstance().openSession());
        this.manageFlows();
        Helper.closeSession();
        LOGGER.debug("Finalizado el proceso de Dado de Baja de Flujos en BPM");
    }

    @Override
    public void shutdown() {
    }
}
