package com.fitbank.common;

import java.sql.*;
import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.SessionImpl;

import com.fitbank.common.conectivity.HbSession;
import com.fitbank.common.logger.FitbankLogger;

/**
 * Clase que contiene métodos para obtener diferentes fechas del sistema.
 *
 * @author Fitbank
 * @version 2.0
 */
public final class ApplicationDates {

    /**
     * Fecha usada para los campos FHASTA como un java.sql.Date.
     */
    public static final Date DEFAULT_EXPIRY_DATE;

    /**
     * Fecha usada para los campos FHASTA como un java.sql.Timestamp.
     */
    public static final Timestamp DEFAULT_EXPIRY_TIMESTAMP;

    /**
     * Constante para escribir logs en la consola.
     */
    private static final Logger LOGGER = FitbankLogger.getLogger();

    static {
        try {
            SimpleDateFormat fd = new SimpleDateFormat("yyyy-MM-dd");
            java.util.Date a = fd.parse("2999-12-31");

            DEFAULT_EXPIRY_DATE = new Date(a.getTime());
            DEFAULT_EXPIRY_TIMESTAMP = new Timestamp(a.getTime());
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    private static long diff = Long.MIN_VALUE;

    private ApplicationDates() {
    }

    /**
     * @return Un java.sql.Date con la fecha de la base de datos.
     */
    public static Date getDBDate() {
        return new Date(getDBTimestamp().getTime());
    }

    /**
     * @return Un java.sql.Timestamp con la fecha de la base de datos.
     */
    public static Timestamp getDBTimestamp() {
        if (diff == Long.MIN_VALUE) {
            calculateTimestampDiff();
        }
        return new Timestamp(new java.util.Date().getTime() - diff);
    }

    private static synchronized void calculateTimestampDiff() {
        if (diff != Long.MIN_VALUE) {
            return;
        }

        Session session = Helper.getSession();
        Dialect dialect = HbSession.getInstance().getDialect();
        String sql;

        if (dialect.getClass().getSimpleName().startsWith("DB2")) {
            sql = "SELECT current timestamp FROM sysibm.sysdummy1";
        } else {
            sql = dialect.getCurrentTimestampSelectString();
        }

        PreparedStatement pst = null;
        ResultSet rst = null;
        Timestamp timestamp = null;
        try {
            pst = ((SessionImpl) session).connection().prepareStatement(sql);
            rst = pst.executeQuery();
            rst.next();
            timestamp = rst.getTimestamp(1);
        } catch (SQLException e) {
            //Problemas al obtener el query de la bdd
            LOGGER.error("Error al obtener la fecha de la bdd", e);
        } finally {
            try {
                if (rst != null) {
                    rst.close();
                }
                if (pst != null) {
                    pst.close();
                }
            } catch (SQLException ex) {
                // No se pudo cerrar el ResulSet o el PreparedStatement
                LOGGER.error("Problemas al cerrar los objetos", ex);
            }
        }

        if (timestamp != null) {
            diff = new java.util.Date().getTime() - timestamp.getTime();
        }
    }

    //<editor-fold defaultstate="collapsed" desc="Métodos deprecados">
    @Deprecated
    private static final ApplicationDates instance = new ApplicationDates();

    /**
     * @return Referencia al singleton de esta clase.
     *
     * @deprecated Usar ApplicationDates directamente
     */
    @Deprecated
    public static ApplicationDates getInstance() {
        return instance;
    }

    /**
     * @return Date 2999-12-31
     *
     * @deprecated Usar ApplicationDates.DEFAULT_EXPIRY_DATE
     */
    @Deprecated
    public static java.sql.Date getDefaultExpiryDate() {
        return DEFAULT_EXPIRY_DATE;
    }

    /**
     * @return Timestamp 2999-12-31
     *
     * @deprecated Usar ApplicationDates.DEFAULT_EXPIRY_TIMESTAMP
     */
    @Deprecated
    public static Timestamp getDefaultExpiryTimestamp() {
        return DEFAULT_EXPIRY_TIMESTAMP;
    }

    /**
     * @return Un java.sql.Date con la fecha de la base de datos.
     *
     * @deprecated Usar ApplicationDates.getDBDate()
     */
    @Deprecated
    public Date getDataBaseDate() {
        return getDBDate();
    }

    /**
     * @param session Sesion sobre la que se va a trabajar
     *
     * @return Un java.sql.Date con la fecha de la base de datos.
     *
     * @deprecated Usar ApplicationDates.getDBDate(Session)
     */
    @Deprecated
    public Date getDataBaseDate(Session session) {
        return getDBDate(session);
    }

    /**
     * @return Un timestamp con la fecha de la base de datos.
     *
     * @deprecated ApplicationDates.getDBTimestamp()
     */
    @Deprecated
    public Timestamp getDataBaseTimestamp() {
        return getDBTimestamp();
    }

    /**
     * @param session Session que se va a usar para consultar.
     *
     * @return java.sql.Timestamp con la fecha de la base de datos.
     *
     * @deprecated Usar ApplicationDates.getDBTimestamp(Session)
     */
    @Deprecated
    public Timestamp getDataBaseTimestamp(Session session) {
        return getDBTimestamp(session);
    }

    /**
     * Metodo que entrega la variable que entrega el timestamp de la base,
     * dependiendo del dialecto de la base que se este utilizando.
     *
     * @return String con la variable o expresión
     *
     * @deprecated Este método no es seguro de llamar. Depende de una
     * combinación muy específica de versiones de hibernate y dialectos.
     */
    @Deprecated
    public String getTimestampVariable() {
        SessionFactoryImpl imp = (SessionFactoryImpl) Helper.getSession().
                getSessionFactory();
        String dialect = imp.getDialect().getClass().getSimpleName();

        if (dialect.startsWith("Oracle")) {
            return "systimestamp";
        } else if (dialect.startsWith("DB2")) {
            return "current timestamp";
        } else if (dialect.startsWith("SQLServer")) {
            return "getdate()";
        } else {
            throw new HibernateException("Dialecto no soportado " + dialect);
        }
    }

    /**
     * @param session Ignorado
     * @return Un java.sql.Date con la fecha de la base de datos.
     * @deprecated Se va usar Helper.getSession() ignorando el parametro
     */
    @Deprecated
    public static Date getDBDate(Session session) {
        return getDBDate();
    }

    /**
     * @param session Ignorado
     * @return java.sql.Timestamp con la fecha de la base de datos.
     * @deprecated Se va usar Helper.getSession() ignorando el parametro
     */
    @Deprecated
    public static Timestamp getDBTimestamp(Session session) {
        return getDBTimestamp();
    }
    //</editor-fold>

}
