package com.sonymusic.dx.persistence.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.Persistence;

public class DxDatabaseUtil {

	private static Logger log = LoggerFactory.getLogger(DxDatabaseUtil.class);

	private static final ThreadLocal<EntityManager> threadEntityManager = new ThreadLocal<EntityManager>();

	protected static String PERSISTENCE_NAME = "dx-gras-persistence";

	public static final String PERSISTENCE_PROPERTY_FILE_HOST = "/SDA/apps/dx/config/persistence.properties";

	public static final String PERSISTENCE_PROPERTY_FILE_LOCAL_HOST = "./src/resources/config/dev/persistence.properties";

	protected static EntityManagerFactory emf;

	protected static boolean inContainer;

	/**
	 * @return true if the DatabaseUtil thinks it is running in-container
	 */
	public static boolean isInContainer() {
		return inContainer;
	}

	/**
	 * Uses the passed in entityManagerFactory, an injected factory, or creates a
	 * new factory.
	 * 
	 * @return EntityManagerFactory
	 */
	private synchronized static EntityManagerFactory getOrCreateEMF() {

		// If we've found the emf before, use that.
		if (emf != null) {
			// log.debug("Using existing persistence factory");
			return emf;
		} else {

			
			// If existing persistence unit not found, assume we are out of container and
			// create a new one.
			try {
				log.info("Creating new persistence factory");

				File propertiesFile = new File(PERSISTENCE_PROPERTY_FILE_HOST);
				if (!propertiesFile.exists())
					propertiesFile = new File(PERSISTENCE_PROPERTY_FILE_LOCAL_HOST);

				if (propertiesFile.exists()) {
					Properties properties = getProperties(propertiesFile);
					emf = Persistence.createEntityManagerFactory(PERSISTENCE_NAME, properties);
				} else {
					System.out.println("no file");
					log.debug("Connecting to {}", PERSISTENCE_NAME);
					log.info(
							"You can override hibernate parameters with a persistence.property file and a -Dpersistence.properties.file=filename jvm parameter");
					emf = Persistence.createEntityManagerFactory(PERSISTENCE_NAME);
				}
			} catch (Throwable ex) {
				log.error("Error creating a new J2SE EntityManager", ex);
				throw new ExceptionInInitializerError(ex);
			}
		}
		return emf;
	}

	/**
	 * Each EntityManager is unique per thread. Calling this method will return the
	 * same EntityManager as previous calls to it from the same thread. If you would
	 * like a fresh EntityManager, use {@link #getNewEntityManager()} to obtain one.
	 * 
	 * @return EntityManager already associated with this thread or a newly created
	 *         one
	 */
	public static EntityManager getEntityManager() {
		EntityManager em = threadEntityManager.get();
		try {
			if (em == null || !em.isOpen()) {
				log.debug("Creating Entity Manager");
				em = getOrCreateEMF().createEntityManager();
				em.setFlushMode(FlushModeType.COMMIT); // flush only at the end of the tx (beware of stale data!)
				threadEntityManager.set(em);
			}
		} catch (Exception e) {
			log.error("Problem getting Entity Manager", e);
			throw new RuntimeException(e);
		}
		return em;
	}

	/**
	 * @return A newly created entity manager
	 */
	public static EntityManager getNewEntityManager() {
		EntityManager em = getOrCreateEMF().createEntityManager();
		em.setFlushMode(FlushModeType.COMMIT);
		return em;
	}

	/**
	 * @return A newly created entity manager
	 */
	public static EntityManager getNewEntityManager(PersistenceUnitType persistenceUnitType) {
		EntityManagerFactory emf = null;
		emf = getOrCreateEMF();

		EntityManager em = emf.createEntityManager();
		em.setFlushMode(FlushModeType.COMMIT);
		return em;
	}

	/**
	 * @param propertyFile the properties file to load
	 * @return The properties specified in the given file
	 * @throws IOException              If file not found, cant load or read
	 *                                  properties file
	 * @throws IllegalArgumentException
	 */
	private static Properties getProperties(File propertiesFile) throws IOException {
		if (propertiesFile == null)
			throw new IllegalArgumentException("Property File cannot be null");
		propertiesFile = new File(propertiesFile.getCanonicalPath());
		if (propertiesFile.isDirectory())
			throw new IllegalArgumentException("Property file is a directory: " + propertiesFile.getPath());
		if (!propertiesFile.exists())
			throw new FileNotFoundException("Can't find properties file: " + propertiesFile.getPath());
		return getProperties(new FileInputStream(propertiesFile));
	}

	private static Properties getProperties(InputStream stream) throws IOException {
		try {
			Properties properties = new Properties();
			properties.load(stream);
			return properties;
		} finally {
			if (stream != null)
				try {
					stream.close();
				} catch (IOException ex) {
				}
		}
	}

	public enum PersistenceUnitType {
		DXGRASPersistence
	}

}
