package com.fp.bpm.actions;

import java.math.BigDecimal;
import java.sql.Date;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.lang.time.StopWatch;

import com.fp.bpm.ActionExecutor;
import com.fp.bpm.helper.TransactionTime;
import com.fp.bpm.save.SaveProcessor;
import com.fp.common.exception.APPException;
import com.fp.common.exception.ExceptionHandler;
import com.fp.common.fin.MessageGenerator;
import com.fp.common.logger.APPLogger;
import com.fp.dto.Request;
import com.fp.dto.Response;
import com.fp.dto.data.SaveData;
import com.fp.dto.helper.FileHelper;
import com.fp.dto.json.Serializer;
import com.fp.dto.save.SaveRequest;
import com.fp.general.exception.GeneralException;
import com.fp.persistence.commondb.helper.FormatDates;

/**
 * Accion que permite el almacenamiento de informacion en la Base de Datos.
 * 
 * @version 2.1
 */
public class SaveAction extends ActionExecutor {

    /**
     * Función que gurda los datos enviados en la base de datos
     * 
     * @param pRequest Peticion de Datos para la Accion
     * @param fileItem Lista de archivos si van a ser procesados por lote
     * @return Respuesta en JSON del proceso del save.
     * @throws Exception
     */
    @Override
    public String execute(String pRequest, List<FileHelper> fileItem) throws Exception {
        Serializer ser = new Serializer(pRequest);
        SaveRequest sr = (SaveRequest) ser.toObject(SaveRequest.class);
        // fija numero de mensaje
        MessageGenerator mg = MessageGenerator.getInstance();
        sr.setJournalId(mg.generateId(sr.getUser()));
        // Log de la transaccion.
        super.request = sr;
        // fija transaccion original
        super.fillOriginalTransaction();
        super.requestToLog();
        // Fin log de transaccion
        sr.put("file", fileItem);
        Response res = new Response(Response.RESPONSE_OK, "OK");

        this.fillThreadData(pRequest, sr);
        if (TransactionTime.verifyTransactionTime(sr, null)) {
            sr.setResponse(res);
            SaveProcessor processor = new SaveProcessor();
            processor.process(sr, res);
        } else {
            res = new Response("CORE-005", "TRANSACCION FUERA DE HORARIO: " + sr.getTransactionModule() + " TRANSACCION: " + sr.getTransactionCode()
                    + " VERSION: " + sr.getTransactionVersion());
        }
        ser = new Serializer(res);
        return ser.toJSON();
    }

    /**
     * Metodo que almacena en SaveData, los datos del saverequest de entrada.
     * 
     * @throws Exception
     */
    private void fillThreadData(String pData, SaveRequest pSaveRequest) throws Exception {
        SaveData.setSaveRequest(pSaveRequest);
        // Almacena string de datos de entrada en el thread local, se utiliza para llenar
        if (SaveData.getOriginalString() == null && pData != null) {
            // En producccion se almacena en el EngineExecutor
            SaveData.setOriginalString(pData);
        }
    }

    public Object process(Request request) throws Exception {
        Response response = new Response(Response.RESPONSE_OK, "OK");
        StopWatch sw = null;
        boolean error = false;
        try {
            sw = new StopWatch();
            sw.start();

            this.execute(request, response);

            sw.stop();

        } catch (Throwable e) {
            error = true;
            try {
                if (sw != null) {
                    sw.stop();
                }
            } catch (IllegalStateException e1) {
                // o hacer nada el stopwatch ya esta detenido
            }
            this.fillError(e, response);
            //retorn excepcion para que no haga commit el ejb savebean
            throw e;
        } finally {
            try {
                if (!error) {
                    super.saveLog(this.getTime(sw), "S");
                } else {
                    super.saveLogWithCommit(this.getTime(sw), "S", response.getResponseCode(), response.getResponseUserMessage(), null, null);
                }
            } catch (Exception e2) {
                APPLogger.getLogger().error("ERROR GRABANDO LOG AUDITORIA \n");
            }
        }
        return response;
    }

    /**
     * Función que gurda los datos enviados en la base de datos
     * 
     * @param pRequest Peticion de Datos para la Accion
     * @throws Exception
     */
    public void execute(Request request, Response response) throws Exception {
        SaveRequest sr = new SaveRequest();
        sr.setJsf(true);
        request.copy(sr);
        request.copyMapValues(sr);
        sr.setResponse(response);
        sr.putAll(request.getModifiedData()); //campos sueltos del request.
        SaveActionHelper sah = new SaveActionHelper();
        sah.addSaveBean(request, sr); // Adiciona sabebean de mantenimiento.
        // fija numero de mensaje
        MessageGenerator mg = MessageGenerator.getInstance();
        sr.setJournalId(mg.generateId(sr.getUser()));
        // Log de la transaccion.
        super.request = sr;
        // fija transaccion original
        super.fillOriginalTransaction();
        super.requestToLog();
        // Fin log de transaccion
        this.fillThreadData(null, sr);
        if (TransactionTime.verifyTransactionTime(sr, null)) {
            SaveProcessor processor = new SaveProcessor();
            processor.process(sr, response);

            sah.addPktoResponse(request, response);
        } else {
            throw new GeneralException("CORE-0054", "TRANSACCIÓN FUERA DE HORARIO MODULO: {0} TRANSACCIÓN: {1} VERSIÓN: {2}", sr.getTransactionModule(),
            		sr.getTransactionCode(), sr.getTransactionVersion());
        }
    }

    /**
     * Obtiene el tiempo de respuesta resultado de la ejecucion de una transaccion.
     * 
     * @param sw Objeto que contiene el tiempo de ejecucion de una transaccion.
     * @return BigDecimal
     * @throws Exception
     */
    private BigDecimal getTime(StopWatch sw) throws Exception {
        Date d = new Date(sw.getTime());
        return new BigDecimal(FormatDates.getInstance().getTimeInSeconds().format(d));
    }

    private void fillError(Throwable e, Response response) throws Exception {
        ExceptionHandler eh = new ExceptionHandler(e, "es");
        APPLogger.getLogger().error("User Message:==>\n" + eh.getUserMessage());
        response.setResponseCode(eh.getCode());
        response.setResponseUserMessage(eh.getUserMessage());
        response.setIsAplicationMessage(eh.isAppexception());

        if ((e instanceof APPException) || (e instanceof SQLException)) {
            response.setResponseStack(eh.getTechnicalMessage());
        } else {
            response.setResponseStack(eh.getStackTrace().length() > 1536 ? eh.getStackTrace().substring(0, 1536) : eh.getStackTrace());
            APPLogger.getLogger().error("StackTrace:==>" + response.getResponseStack());
        }
    }

}
