package hib.fs.db.ora;

import hib.fs.orm.EstablishEntityManager;
import hib.fs.utils.ColorUtilConstants;
import io.quarkus.runtime.Quarkus;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serial;
import java.io.Serializable;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.mapping.Table;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import java.util.Properties;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
// import hib.fs.db.emf.MetadataExtractorIntegrator;
import org.hibernate.jpa.boot.spi.IntegratorProvider;
import java.util.List;


public class DodexDatabaseOracle extends DbOracle implements Serializable {
  @Serial
  private static final long serialVersionUID = 1L;
  private final static Logger logger = LoggerFactory.getLogger(DodexDatabaseOracle.class.getName());

  private static EntityManager em;
  private static EntityManagerFactory emf;
  static protected String dbName;

  public DodexDatabaseOracle() {
    super();
  }

  public void entityManagerSetup() {
      SessionFactory sessionFactory;
      Session session;

      try {
          if(EstablishEntityManager.getOraEmf() == null) {
              EstablishEntityManager establishEntityManager = new EstablishEntityManager();
              establishEntityManager.runGenerator(null);
          }
          em = EstablishEntityManager.getOraEmf().createEntityManager();

          session = em.unwrap(Session.class);
          if (session == null) {
              emf = EstablishEntityManager.getOraEmf();
              sessionFactory = EstablishEntityManager.getOraEmf().createEntityManager().unwrap(SessionFactory.class);
          }
      } catch (Exception ioe) {
          throw new RuntimeException(ioe.getMessage());
      }

      assert session != null;
      Map<String, Object> properties = session.getProperties() != null ? session.getProperties(): new HashMap<>();
      Object sessionDbname = properties.get("database");
      dbName = sessionDbname != null ? sessionDbname.toString() : "dodex";
      Object mode = properties.get("mode");
  }

  public void entityManagerSetup(EntityManagerFactory emf) {
  }

  public void configDatabase() {
    String[] types = {"TABLE"};

    Session session = em.unwrap(Session.class);

    session.doWork(connection -> {
      try {
        connection.setAutoCommit(false);
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        boolean didCreateATable = false;

        ResultSet rsDbname = databaseMetaData.getTables(dbName, null, "USERS", types);

        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("USERS"));
          connection.createStatement().execute(getCreateTable("NAMEIDX"));
          connection.createStatement().execute(getCreateTable("PASSWORDIDX"));
          didCreateATable = true;
          logger.info("{}Users Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Users Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "MESSAGES", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("MESSAGES"));
          didCreateATable = true;
          logger.info("{}Messages Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Messages Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "UNDELIVERED", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("UNDELIVERED"));
          didCreateATable = true;
          logger.info("{}Undelivered Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Undelivered Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "GROUPS", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("GROUPS"));
          didCreateATable = true;
          logger.info("{}Groups Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Groups Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "MEMBER", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("MEMBER"));
          didCreateATable = true;
          logger.info("{}Member Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Member Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "GOLFER", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("GOLFER"));
          didCreateATable = true;
          logger.info("{}Golfer Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Golfer Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "COURSE", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("COURSE"));
          didCreateATable = true;
          logger.info("{}Course Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Course Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "RATINGS", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("RATINGS"));
          didCreateATable = true;
          logger.info("{}Ratings Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Ratings Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        rsDbname = databaseMetaData.getTables(dbName, null, "SCORES", types);
        if (!rsDbname.next()) {
          connection.createStatement().execute(getCreateTable("SCORES"));
          didCreateATable = true;
          logger.info("{}Scores Table Created.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        } else {
            logger.info("{}Scores Table Found.{}", ColorUtilConstants.GREEN, ColorUtilConstants.RESET);
        }
        if (didCreateATable) {
          ResultSet rsSchemas = databaseMetaData.getCatalogs();
          while (rsSchemas.next()) {
            if (dbName.equals(rsSchemas.getString(1))) {
              logger.warn("{}Used database: '{}' to Create Tables.{}",
                ColorUtilConstants.YELLOW, dbName, ColorUtilConstants.RESET);
            }
          }
        }
        rsDbname.close();
        connection.commit();
        connection.close();
        Quarkus.asyncExit();
      } catch (SQLException se) {
        throw new RuntimeException(se.getMessage());
      }
    });
  }

  public EntityManagerFactory getEmf() {
    return emf;
  }
}
