package com.fp.general.score.save;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Date;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.lang.StringUtils;

import com.fp.common.exception.ExceptionHandler;
import com.fp.common.logger.APPLogger;
import com.fp.dto.save.SaveRequest;
import com.fp.loan.exception.LoanException;
import com.fp.persistence.commondb.PersistenceHelper;
import com.fp.persistence.commondb.PersistenceManager;
import com.fp.persistence.commondb.data.SessionData;
import com.fp.persistence.commondb.data.ThreadFacade;
import com.fp.persistence.commondb.helper.FormatDates;
import com.fp.persistence.pgeneral.score.TgeneScoreAdjustment;
//import com.fp.persistence.ploan.acco.TloanPaymentBankProcess;

/**
 * Clase que se encarga de procesar un archivo con información de pagos de créditos procedente de una canal de
 * recaudación
 * 
 * @author Omar Villanueva
 * @version 2.1
 */
public class UploadFileScoreThread extends Thread {

    private final Integer company;

    /* Arvhivo a procesar */
    private final File file;

    /* Institución financiera a la que pertenece el archivo */
    private final String bank;

    /* Directorio destino para el archivo luego de procesarlo */
    private final StringBuilder targetPath;

    /*
     * Map para los formatos de nombres de archivos de las instituciones financieras parametrizadas en
     * TLOANPAYMENTACCOBANK
     */
    private final Map<String, String> mFileName;

    /*
     * Map para los numeros de hilos que se puede procesar en paralelo por cada una de las instituciones financieras
     * parametrizadas en TLOANPAYMENTACCOBANK
     */
    private final Map<String, Integer> mThreads;

    /* String para almacenar el log de errores en la carga de los archivos para mostrar al usuario en el front end */
    private final StringBuilder sbLog;

    /* Separador de linea */
    private final String new_line = System.getProperty("line.separator");
    
    private final SaveRequest sr;
    
    

    /**
     * Constructor
     * 
     * @param company
     * @param file
     * @param bank
     * @param targetPath
     * @param mFileName
     * @param mThreads
     * @param sbLog
     */
    public UploadFileScoreThread(Integer company, File file, String bank, StringBuilder targetPath, Map<String, String> mFileName,
            Map<String, Integer> mThreads, StringBuilder sbLog, SaveRequest sr) {
        super();
        this.company = company;
        this.file = file;
        this.bank = bank;
        this.targetPath = targetPath;
        this.mFileName = mFileName;
        this.mThreads = mThreads;
        this.sbLog = sbLog;
        this.sr=sr;
    }

    @Override
    public void run() {
        // boolean success = true;
        try {
            SessionData s = new SessionData();
            s.setCompany(company);
            ThreadFacade.setSaveRequest(s);
            PersistenceHelper.setEntityManager(PersistenceManager.getInstance().createEntityManagerLocal());
            PersistenceHelper.beginNewTransaction();
            /* Procesar archivo */
            process();
            PersistenceHelper.commitNewTransaction();
            /* Mover el archivo si la carga fue exitosa */
            moveFile();
        } catch (Exception e) {
            // success = false;
            PersistenceHelper.rollbackNewTransaction();
            ExceptionHandler eh = new ExceptionHandler(e, "es");
            APPLogger.getLogger().error("codigo ==>" + eh.getCode());
            APPLogger.getLogger().error("user ==>" + eh.getUserMessage());
            APPLogger.getLogger().error("technical ==>" + eh.getTechnicalMessage());
            APPLogger.getLogger().error("stacktrace ==>" + eh.getStackTrace());
        } finally {
            PersistenceHelper.closeSession();
        }
        // /* Mover el archivo si la carga fue exitosa */
        // if (success) {
        // try {
        // moveFile();
        // } catch (Exception e) {
        // e.printStackTrace();
        // }
        // }
    }

    /**
     * Método que procesa el archivo. Carga la data en la tabla TGENESCOREADJUSTMENT(---TLOANPAYMENTBANKPROCESS). Lanza un hilo por cada linea
     * que contiene el arvhivo. Se pueden lanzar varios hilos en paralelo. El numero máximo de hilos esta determinado
     * por el parametro threads fijado en la tabla TLOANPAYMENTACCOBANK
     * 
     * @throws Exception
     */
    private void process() throws Exception {
        /* Verificar si el archivo ya fue cargado anteriormente */
        //String fileExist = TloanPaymentBankProcess.find(PersistenceHelper.getEntityManager(), company, file.getName());
        String fileExist = TgeneScoreAdjustment.find(PersistenceHelper.getEntityManager(), company, file.getName());
        
        if (fileExist != null) {
            sbLog.append(new LoanException("LOAN-0049", "EL ARCHIVO: {0} YA FUE CARGADO ANTERIORMENTE", file.getName()).getMessage())
                    .append(new_line);
            throw new LoanException("LOAN-0049", "EL ARCHIVO: {0} YA FUE CARGADO ANTERIORMENTE", file.getName());
        }
        /* Verificar si el numero de hilos esta definido */
        if (mThreads.get(bank) == null || mThreads.get(bank).compareTo(0) <= 0) {
            /* Agrega el mensaje al log de errores */
            sbLog.append(
                    new LoanException("LOAN-0041", "NUMERO DE HILOS NO DEFINIDO EN TLOANPAYMENTACCOBANK PARA INSTITUCION FINANCIERA: {0}", bank)
                            .getMessage()).append(new_line);
            throw new LoanException("LOAN-0041", "NUMERO DE HILOS NO DEFINIDO EN TLOANPAYMENTACCOBANK PARA INSTITUCION FINANCIERA: {0}", bank);
        }
        ExecutorService pool = Executors.newFixedThreadPool(mThreads.get(bank));
        /* Obtener la fecha de pago del archivo a procesar */
        Date paymentDate = getDateFromFile();
        try {
            BufferedReader input = new BufferedReader(new FileReader(file));
            try {
                String line = null;
                int sequence = 0;
                Timestamp realDate = FormatDates.getInstance().getDataBaseTimestamp();
                while ((line = input.readLine()) != null) {
                    sequence++;
                    /* Se lanza un hilo por cada linea del archivo que se debe procesar */
                    UploadLineScoreThread ult = new UploadLineScoreThread(company, file, bank, line, sequence, realDate, paymentDate, sr);
                    pool.execute(ult);
                }
            } catch (IOException ioe) {
                throw ioe;
            } finally {
                input.close();
            }
        } catch (IOException ioe) {
            throw ioe;
        }
        pool.shutdown();
        boolean isfinish = pool.isTerminated();
        while (!isfinish) {
            isfinish = pool.isTerminated();
            Thread.sleep(600);
        }
    }

    /**
     * Método que obtiene la fecha desde el arvhivo proporcionado por el canal de recaudación. Esta es la fecha de pago
     * de la cuota del crédito
     * 
     * @return
     * @throws Exception
     */
    private Date getDateFromFile() throws Exception {
        Date date = null;
        String fileName = StringUtils.remove(mFileName.get(bank), '-');
        int positionDate = fileName.indexOf("ddMM");
        /* Procesa archivos que contengan el formato ddMM */
        if (positionDate < 0) {
            /* Agrega el mensaje al log de errores */
            sbLog.append(
                    new LoanException("LOAN-0038", "FECHA INCORRECTAMENTE DEFINIDA EN TLOANPAYMENTACCOBANK PARA INSTITUCIÓN FINANCIERA: {0}", bank)
                            .getMessage()).append(new_line);
            throw new LoanException("LOAN-0038", "FECHA INCORRECTAMENTE DEFINIDA EN TLOANPAYMENTACCOBANK PARA INSTITUCIÓN FINANCIERA: {0}", bank);
        }
        if (bank.compareTo("SP") == 0) {
            String formatDate = mFileName.get(bank).substring(positionDate, positionDate + 10);
            String strDate = file.getName().trim().substring(positionDate, positionDate + 10);
            date = getDateByFormat(strDate, formatDate);
        }
  
        return date;
    }

    /**
     * Método que devuelve la fecha según el formato requerido
     * 
     * @param pStrDate
     * @param pFormat
     * @return
     * @throws Exception
     */
    private Date getDateByFormat(String pStrDate, String pFormat) throws Exception {
        Date date = null;
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pFormat);
            date = new Date(simpleDateFormat.parse(pStrDate).getTime());
        } catch (Exception e) {
            /* Agrega el mensaje al log de errores */
            sbLog.append(
                    new LoanException(
                            "LOAN-0050",
                            "EL FORMATO DE FECHA DEL ARCHIVO: {0} NO COINCIDE CON EL DEFINIDO EN TLOANPAYMENTACCOBANK PARA INSTITUCIÓN FINANCIERA: {1}",
                            file.getName(), bank).getMessage()).append(new_line);
            throw new LoanException("LOAN-0050",
                    "EL FORMATO DE FECHA DEL ARCHIVO: {0} NO COINCIDE CON EL DEFINIDO EN TLOANPAYMENTACCOBANK PARA INSTITUCIÓN FINANCIERA: {1}",
                    file.getName(), bank);
        }

        return date;
    }

    /**
     * Método que mueve el archivo ya procesado al directorio destino
     * 
     * @throws Exception
     */
    private void moveFile() throws Exception {
        /* Directorio destino */
        File targetDir = new File(targetPath.toString());

        /* Mover el archivo al nuevo directorio */
        boolean success = file.renameTo(new File(targetDir, file.getName()));
        if (!success) {
            /* Agrega el mensaje al log de errores */
            sbLog.append(new LoanException("LOAN-0040", "EL ARCHIVO {0} ESTA SIENDO USADO POR OTRO PROCESO", file.getName()).getMessage()).append(
                    new_line);
            throw new LoanException("LOAN-0040", "EL ARCHIVO {0} ESTA SIENDO USADO POR OTRO PROCESO", file.getName());
        }
    }
}
