package jesse.keeblarcraft.ConfigMgr;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import jesse.keeblarcraft.ConfigMgr.SQLTypeSupport.VALID_SQL_TYPE;
import jesse.keeblarcraft.Utils.CommonStructures.Pair;

public class SQLConfig {
    private static SQLConfig static_inst;
    private String dbName;
    private String dbUser;
    private String dbPass;
    private String dbAddr;
    private Boolean connected = false;
    private Connection conn = null; // Actual connection object

    private enum SQL_RUN_TYPE {
        GENERAL,
        QUERY,
        UPDATE
    }

    // Direct return object in relation to the SQL_RUN_TYPE of the SQL command that was used. Only one will
    // be filled out properly for what you run - the other values will be null and not cleared!
    private class SQLRunReturn {
        Boolean generalRet;
        ResultSet queryReturn;
        Integer updateReturn;
    }

    public static SQLConfig GetInstance() {
        if (static_inst == null) {
            static_inst = new SQLConfig();
        }

        return static_inst;
    }


    public SQLConfig() {
        connected = false;
        try {
            // According to some random online tutorial; this loads the driver!
            Class.forName("com.mysql.cj.jdbc.Driver");
            connected = true;
        } catch (Exception e) {
            System.out.println("Could not find the proper SQL JDBC Drivers. Cannot connect to SQL Config");
            e.printStackTrace();
        }

        if (connected) {
            try {
                dbName = GeneralConfig.GetInstance().GetSQLDatabase();
                dbUser = GeneralConfig.GetInstance().GetSQLUsername();
                dbPass = GeneralConfig.GetInstance().GetSQLPassword();
                dbAddr = "jdbc:mysql://" + GeneralConfig.GetInstance().GetSQLAddress() + "/" + dbName;
                conn = DriverManager.getConnection(dbAddr, dbUser, dbPass);
            } catch (SQLException e) {}
        }
    }

    private SQLRunReturn RunCommand(String sqlCommand, SQL_RUN_TYPE type) {
        SQLRunReturn sqlData = new SQLRunReturn();
        // If conn is null; we may need to reconnect before running the command
        if (conn == null) {
            Connect();
        }

        if (conn != null) {
            try {
                Statement statement = conn.createStatement();

                switch(type) {
                    case GENERAL:
                        sqlData.generalRet = statement.execute(sqlCommand);
                        break;
                    case QUERY:
                        sqlData.queryReturn = statement.executeQuery(sqlCommand);
                        break;
                    case UPDATE:
                        sqlData.updateReturn = statement.executeUpdate(sqlCommand);
                        break;
                }
            } catch (SQLException e) {
                System.out.println(e.getSQLState());
            }
        }

        return sqlData;
    }

    // Re-attempt the connection
    public Boolean Connect() {
        if (conn != null) {
            try {
                conn.close();
            } catch (Exception e) {}
        }

        Boolean success = false;
        try {
            conn = DriverManager.getConnection(dbAddr, dbUser, dbPass);
            success = true;
        } catch (Exception e) {}
        return success;
    }

    public Boolean IsConnected() {
        return conn != null && this.connected;
    }

    // The case is converted to upper case automatically to be case insensitive
    public Boolean DoesTableExist(String name) {
        boolean tableExists = false;
        if (conn != null) {
            try (ResultSet rs = conn.getMetaData().getTables(null, null, name, null)) {
                while (rs.next()) {
                    String tName = rs.getString("TABLE_NAME");
                    if (tName != null && tName.equals(name)) {
                        tableExists = true;
                        break;
                    }
                }
            } catch (SQLException e) {}
        }
        return tableExists;
    }

    public Boolean MakeNonNullable(String table, String columnName) {
        return false;
    }

    public Integer AddPrimaryKey(String tableName, String primaryKeyColumn) {
        SQLRunReturn success = new SQLRunReturn();

        if (DoesTableExist(tableName)) {
            String sqlCmd = "ALTER TABLE " + tableName + " ADD PRIMARY KEY (" + primaryKeyColumn + ");";

            success = RunCommand(sqlCmd, SQL_RUN_TYPE.UPDATE);
        }

        return success.updateReturn;
    }

    public Integer AddForeignKey(String primaryTableName, String primaryColumnName, String foreignTableName, String foreignKeyColumn) {
        SQLRunReturn success = new SQLRunReturn();
        Boolean primaryTableExists = DoesTableExist(primaryTableName);
        Boolean foreignTableExists = DoesTableExist(foreignTableName);

        if (primaryTableExists && foreignTableExists) {
            String sqlCmd = "ALTER TABLE " + primaryTableName + " ADD FOREIGN KEY (" + primaryColumnName + ") REFERENCES " + foreignTableName + " (" + foreignKeyColumn + ")";
            success = RunCommand(sqlCmd, SQL_RUN_TYPE.UPDATE);
        }
        return success.updateReturn;
    }

    // Might fix heap pollution decay in future with enum types or something. For now we assume the user isn't horrifically stupid and will give a SQL-able type
    public Boolean CreateTable(String tableName, Pair<String, VALID_SQL_TYPE>... columnPairs) {
        Boolean success = false;

        if (conn != null && !DoesTableExist(tableName.toUpperCase())) {
            String sqlCommand = "CREATE TABLE " + tableName.toUpperCase() + "(";
            for (Pair<String, VALID_SQL_TYPE> colPair : columnPairs) {
                sqlCommand = sqlCommand + " " + colPair.GetKey() + " " + SQLTypeSupport.GetSqlStrForType(colPair.GetValue()) + ",";
            }

            // Strip the leading comma and append the ending ');' on the SQL statement
            sqlCommand = sqlCommand.substring(0, sqlCommand.length() - 1) + ");";

            SQLRunReturn rs = RunCommand(sqlCommand, SQL_RUN_TYPE.GENERAL);

            try {
                while (rs.queryReturn.next()) {

                }
            } catch (Exception e) {}
        }
        return success;
    }
}
