package com.fitbank.web.uci;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import com.fitbank.common.FileHelper;
import com.fitbank.common.helper.XMLParser;
import com.fitbank.dto.GeneralResponse;
import com.fitbank.dto.management.Criterion;
import com.fitbank.dto.management.Detail;
import com.fitbank.dto.management.Field;
import com.fitbank.dto.management.Record;
import com.fitbank.dto.management.Table;
import com.fitbank.uci.client.UCIClient;
import com.fitbank.util.Debug;
import com.fitbank.web.data.PedidoWeb;
import com.fitbank.web.data.RespuestaWeb;
import com.fitbank.web.exceptions.ErrorWeb;
import com.fitbank.web.procesos.Registro;
import com.fitbank.web.providers.HardDiskWebPageProvider;
import com.fitbank.web.servlets.Procesador;
import com.fitbank.web.uci.db.TransporteDBUCI;
import com.fitbank.webpages.WebPageEnviroment;

public class EnlaceUCI {

    public RespuestaWeb procesar(PedidoWeb pedido) {
        TransporteDBUCI tdbuci = (TransporteDBUCI) pedido.getTransporteDB();
        Detail detail = tdbuci.getDetail();
        this.copiarEjecutadoPor(detail);

        if (WebPageEnviroment.getDebug()) {
            detail.findFieldByNameCreate("__DEBUG__").setValue("true");
        } else {
            detail.removeField("__DEBUG__");
        }

        tdbuci.save();

        Registro.getRegistro().salvarDatosEntrada(pedido.getTransporteDB());

        String mode = StringUtils.defaultIfEmpty(Procesador.getMode(), "");

        if ("save".equals(mode)) {
            save(tdbuci);
        }

        TransporteDBUCI datosRespuesta;
        if ("replay".equals(mode)) {
            datosRespuesta = new TransporteDBUCI(load(detail));
        } else {
            datosRespuesta = new TransporteDBUCI(fix(UCIClient.send(detail)));

            if ("save".equals(mode)) {
                save(detail, datosRespuesta);
            }
        }

        Registro.getRegistro().salvarDatosSalida(datosRespuesta);

        RespuestaWeb respuesta = new RespuestaWeb(datosRespuesta, pedido);
        respuesta.setTipoPedido(pedido.getTipoPedido());
        this.limpiarEjecutadoPor(detail);
        this.limpiarEjecutadoPor(((TransporteDBUCI) respuesta.getTransporteDB()).getDetail());
        return respuesta;
    }

    /**
     * En caso de que venga un Detail sin alias en campos asumir que son campos
     * de la tabla principal.
     *
     * @param detail Detail a ser procesado
     *
     * @return el mismo Detail despues de ser procesado
     */
    private Detail fix(Detail detail) {
        GeneralResponse res = detail.getResponse();
        if (res != null && res.getUserMessage() != null) {
            String code = res.getCode() + ": ";
            String mes = res.getUserMessage();

            if (mes.startsWith(code)) {
                mes = mes.substring(code.length());
            }

            if (res.getUserMessage().endsWith(" <*>")) {
                mes = mes.substring(0, mes.length() - 4);
            }

            res.setUserMessage(mes);
        }

        if (Conversor.JOIN_PROCESS_TYPE.equals(detail.getProcessType())) {
            for (Table table : detail.getTables()) {
                table.addMissing();
            }
        }

        return detail;
    }

    private void save(TransporteDBUCI tdbuci) {
        save(tdbuci.toString(), tdbuci.getDetail(), "entrada");
    }

    private void save(Detail detail, TransporteDBUCI datosRespuesta) {
        save(datosRespuesta.toString(), detail, "salida");
    }

    private void save(String xml, Detail detail, String var) {
        File path = new File(HardDiskWebPageProvider.getPath(
                detail.getSubsystem(), detail.getTransaction(), "test.d"));
        path.mkdirs();

        File file = new File(path, getSignature(detail) + "-" + var + ".xml");
        try {
            IOUtils.write(xml, new FileOutputStream(file), "UTF-8");
        } catch (Exception ex) {
            Debug.error(ex);
        }
    }

    private Detail load(Detail detail) {
        File path = new File(HardDiskWebPageProvider.getPath(
                detail.getSubsystem(), detail.getTransaction(), "test.d"));
        path.mkdirs();

        File file = new File(path, getSignature(detail) + "-salida.xml");

        try {
            String data = FileHelper.readStream(new FileInputStream(file));

            return new Detail(new XMLParser(data));
        } catch (Exception e) {
            throw new ErrorWeb("No se encontró el detail correspondiente", e);
        }
    }

    /**
     * Obtiene una firma del detail única para poder identificarlo.
     *
     * @param detail
     * @return
     */
    private String getSignature(Detail detail) {
        int hash = 0;
        int i = 0;

        for (Table table : detail.getTables()) {
            // Tomar el alias, registro actual y página de la tabla para el cálculo
            hash += hash(table.getAlias());
            hash += hash(table.getCurrentRecord());
            hash += hash(table.getPageNumber());
            hash += 1 << i++;

            for (Criterion criterion : table.getCriteria()) {
                // Tomar alias y nombre del criterio
                hash += hash(criterion.getAlias());
                hash += hash(criterion.getName());
                hash += hash(criterion.getValue());
                hash += hash(criterion.getOrder());
                hash += 1 << i++;
            }

            for (Record record : table.getRecords()) {
                // Tomar el número de registro
                hash += hash(record.getNumber());
                hash += 1 << i++;

                for (Field field : record.getFields()) {
                    // Tomar el alias y nombre del campo
                    hash += hash(field.getAlias());
                    hash += hash(field.getName());
                    hash += hash(field.getFunctionName());
                    hash += hash(field.getValue());
                    hash += 1 << i++;
                }
            }
        }

        for (Field field : detail.getFields()) {
            // Tomar el alias y nombre del campo
            hash += hash(field.getName());
            hash += hash(field.getValue());
            hash += 1 << i++;
        }

        return String.format("%s%s-%s-%s", detail.getSubsystem(), detail.
                getTransaction(), detail.getType(), hash);
    }

    public int hash(Object o) {
        if (o == null) {
            return 0;
        } else {
            return o.hashCode();
        }
    }

    /**
     * Asigna el valor de ejecutadoPor al detail basado en un campo de control
     *
     * @param detail
     */
    private void copiarEjecutadoPor(Detail detail) {
        detail.setExecutedBy(detail.findFieldByNameCreate("EJECUTADOPOR")
                .getStringValue());
    }

    private void limpiarEjecutadoPor(Detail detail) {
        detail.setExecutedBy("F");
        detail.findFieldByNameCreate("EJECUTADOPOR").setValue("F");
    }
}