package com.mzh.library.util;

import com.mzh.library.exception.DaoException;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public final class JdbcUtil {
    private static final String CONFIG_FILE = "db.properties";
    private static final String DEFAULT_DRIVER = "com.mysql.cj.jdbc.Driver";

    @FunctionalInterface
    public interface TransactionCallback<T> {
        T execute(Connection connection) throws SQLException;
    }

    private JdbcUtil() {
    }

    public static Connection getConnection() {
        Properties properties = loadProperties();
        String driver = properties.getProperty("db.driver", DEFAULT_DRIVER);
        String url = required(properties, "db.url");
        String username = required(properties, "db.username");
        String password = required(properties, "db.password");

        try {
            Class.forName(driver);
            return DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException | SQLException ex) {
            throw new DaoException("Unable to open database connection", ex);
        }
    }

    public static <T> T executeInTransaction(TransactionCallback<T> callback) {
        try (Connection connection = getConnection()) {
            connection.setAutoCommit(false);
            try {
                T result = callback.execute(connection);
                connection.commit();
                return result;
            } catch (SQLException ex) {
                rollbackQuietly(connection);
                throw new DaoException("Unable to complete database transaction", ex);
            } catch (RuntimeException ex) {
                rollbackQuietly(connection);
                throw ex;
            }
        } catch (SQLException ex) {
            throw new DaoException("Unable to complete database transaction", ex);
        }
    }

    private static void rollbackQuietly(Connection connection) {
        try {
            connection.rollback();
        } catch (SQLException ignored) {
            // Preserve the original transaction failure for callers and logs.
        }
    }

    private static Properties loadProperties() {
        try (InputStream inputStream = Thread.currentThread()
            .getContextClassLoader()
            .getResourceAsStream(CONFIG_FILE)) {
            if (inputStream == null) {
                throw new DaoException("Missing database configuration file: " + CONFIG_FILE, null);
            }

            Properties properties = new Properties();
            properties.load(inputStream);
            return properties;
        } catch (IOException ex) {
            throw new DaoException("Unable to read database configuration", ex);
        }
    }

    private static String required(Properties properties, String key) {
        String value = properties.getProperty(key);
        if (value == null || value.trim().isEmpty()) {
            throw new DaoException("Missing database configuration value: " + key, null);
        }
        return value.trim();
    }
}
