package net.viper.status.modules.economy;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.plugin.Plugin;

import java.sql.*;
import java.util.UUID;
import java.util.logging.Logger;

/**
 * Verwaltet die MySQL-Verbindung (HikariCP) und die Tabelle bc_accounts.
 *
 * Fixes:
 *  - balance-Spalte als DOUBLE(30,2) statt VARCHAR → kompatibel mit NexEco & SurvivalPlus
 *  - atomare Transaktion f\u00fcr withdraw+deposit → kein Geldverlust bei Absturz
 *  - FOR UPDATE Lock → kein Race-Condition-Bug bei gleichzeitigen \u00dcberweisungen
 */
public class EconomyDatabase {

    private static final String TABLE        = "bc_accounts";
    private static final String TABLE_NAMES  = "bc_player_names";

    private final Logger log;
    private HikariDataSource dataSource;

    public EconomyDatabase(Plugin plugin, String host, int port, String database, String user, String password) {
        this.log = plugin.getLogger();

        HikariConfig cfg = new HikariConfig();
        java.util.logging.Logger.getLogger("com.zaxxer.hikari").setLevel(java.util.logging.Level.WARNING);
        cfg.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database
                + "?useSSL=false&autoReconnect=true&characterEncoding=UTF-8&useUnicode=true"
                + "&allowPublicKeyRetrieval=true");
        cfg.setUsername(user);
        cfg.setPassword(password);
        cfg.setMaximumPoolSize(5);
        cfg.setMinimumIdle(1);
        cfg.setConnectionTimeout(10_000);
        cfg.setIdleTimeout(600_000);
        cfg.setMaxLifetime(1_800_000);
        cfg.setPoolName("StatusAPI-Economy");
        cfg.addDataSourceProperty("cachePrepStmts",        "true");
        cfg.addDataSourceProperty("prepStmtCacheSize",     "250");
        cfg.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");

        try {
            dataSource = new HikariDataSource(cfg);
        } catch (Exception e) {
            log.severe("[Economy] MySQL-Verbindung fehlgeschlagen: " + e.getMessage());
            return;
        }

        // ── bc_accounts: balance als DOUBLE – kompatibel mit NexEco & SurvivalPlus ──
        try (Connection con = dataSource.getConnection(); Statement st = con.createStatement()) {
            st.executeUpdate(
                "CREATE TABLE IF NOT EXISTS `" + TABLE + "` (" +
                "  `player_name` VARCHAR(36)   NOT NULL," +
                "  `balance`     DOUBLE(30,2)  NOT NULL DEFAULT 0.00," +
                "  PRIMARY KEY (`player_name`)" +
                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
            );
            // Falls Tabelle existiert aber balance noch VARCHAR ist → konvertieren
            st.executeUpdate(
                "ALTER TABLE `" + TABLE + "` " +
                "MODIFY COLUMN `balance` DOUBLE(30,2) NOT NULL DEFAULT 0.00"
            );
        } catch (SQLException e) {
            // ALTER schl\u00e4gt fehl wenn Typ bereits korrekt ist – kein Problem
            if (!e.getMessage().contains("Duplicate") && !e.getMessage().contains("doesn't exist")) {
                log.warning("[Economy] Tabellen-Setup bc_accounts: " + e.getMessage());
            }
        }

        // ── bc_player_names ────────────────────────────────────────────────────
        try (Connection con = dataSource.getConnection(); Statement st = con.createStatement()) {
            st.executeUpdate(
                "CREATE TABLE IF NOT EXISTS `" + TABLE_NAMES + "` (" +
                "  `uuid`    VARCHAR(36) NOT NULL PRIMARY KEY," +
                "  `name`    VARCHAR(16) NOT NULL," +
                "  `updated` BIGINT      NOT NULL" +
                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
            );
            if (StatusAPI.DEBUG) log.info("[Economy] MySQL verbunden – Tabellen bereit.");
        } catch (SQLException e) {
            log.severe("[Economy] Tabellen-Setup bc_player_names fehlgeschlagen: " + e.getMessage());
        }
    }

    public boolean isConnected() {
        return dataSource != null && !dataSource.isClosed();
    }

    public void close() {
        if (dataSource != null && !dataSource.isClosed()) dataSource.close();
    }

    // ── Kontostand ────────────────────────────────────────────────────────────

    /** L\u00e4dt den Kontostand direkt aus der DB. Gibt -1 zur\u00fcck wenn kein Eintrag. */
    public double load(UUID uuid) {
        if (!isConnected()) return -1;
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(
                "SELECT `balance` FROM `" + TABLE + "` WHERE `player_name` = ?")) {
            ps.setString(1, uuid.toString());
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) return rs.getDouble("balance");
            }
        } catch (SQLException e) {
            log.warning("[Economy] Load fehlgeschlagen f\u00fcr " + uuid + ": " + e.getMessage());
        }
        return -1;
    }

    /** Schreibt einen Kontostand in die DB (INSERT oder UPDATE). */
    public void save(UUID uuid, double balance) {
        if (!isConnected()) return;
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(
                "INSERT INTO `" + TABLE + "` (`player_name`, `balance`) VALUES (?, ?) " +
                "ON DUPLICATE KEY UPDATE `balance` = VALUES(`balance`)")) {
            ps.setString(1, uuid.toString());
            ps.setDouble(2, balance);
            ps.executeUpdate();
        } catch (SQLException e) {
            log.warning("[Economy] Save fehlgeschlagen f\u00fcr " + uuid + ": " + e.getMessage());
        }
    }

    /**
     * Atomare \u00dcberweisung von → to.
     * Nutzt eine SQL-Transaktion mit FOR UPDATE Lock – race-condition-sicher.
     * Gibt false zur\u00fcck wenn Sender nicht genug Guthaben hat.
     */
    public boolean transfer(UUID from, UUID to, double amount, double startBalance) {
        if (!isConnected()) return false;
        Connection con = null;
        try {
            con = dataSource.getConnection();
            con.setAutoCommit(false);

            // Sender sperren und Balance lesen
            double fromBalance;
            try (PreparedStatement ps = con.prepareStatement(
                "SELECT `balance` FROM `" + TABLE + "` WHERE `player_name` = ? FOR UPDATE")) {
                ps.setString(1, from.toString());
                try (ResultSet rs = ps.executeQuery()) {
                    fromBalance = rs.next() ? rs.getDouble("balance") : startBalance;
                }
            }

            if (fromBalance < amount) { con.rollback(); return false; }

            // Sender abziehen
            try (PreparedStatement ps = con.prepareStatement(
                "INSERT INTO `" + TABLE + "` (`player_name`, `balance`) VALUES (?, ?) " +
                "ON DUPLICATE KEY UPDATE `balance` = VALUES(`balance`)")) {
                ps.setString(1, from.toString());
                ps.setDouble(2, fromBalance - amount);
                ps.executeUpdate();
            }

            // Empf\u00e4nger gutschreiben (Konto anlegen falls n\u00f6tig)
            try (PreparedStatement ps = con.prepareStatement(
                "INSERT INTO `" + TABLE + "` (`player_name`, `balance`) VALUES (?, ?) " +
                "ON DUPLICATE KEY UPDATE `balance` = `balance` + ?")) {
                ps.setString(1, to.toString());
                ps.setDouble(2, startBalance + amount);
                ps.setDouble(3, amount);
                ps.executeUpdate();
            }

            con.commit();
            return true;

        } catch (SQLException e) {
            log.warning("[Economy] Transfer fehlgeschlagen: " + e.getMessage());
            try { if (con != null) con.rollback(); } catch (SQLException ex) { /* ignore */ }
            return false;
        } finally {
            try { if (con != null) con.close(); } catch (SQLException ex) { /* ignore */ }
        }
    }

    // ── Name-Lookup ───────────────────────────────────────────────────────────

    public void saveNameMapping(UUID uuid, String name) {
        if (!isConnected()) return;
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(
                "INSERT INTO `" + TABLE_NAMES + "` (`uuid`, `name`, `updated`) VALUES (?, ?, ?) " +
                "ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `updated` = VALUES(`updated`)")) {
            ps.setString(1, uuid.toString());
            ps.setString(2, name);
            ps.setLong(3, System.currentTimeMillis());
            ps.executeUpdate();
        } catch (SQLException e) {
            log.warning("[Economy] Name-Mapping fehlgeschlagen: " + e.getMessage());
        }
    }

    public UUID findUUIDByNameOwn(String name) {
        if (!isConnected()) return null;
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(
                "SELECT `uuid` FROM `" + TABLE_NAMES + "` WHERE `name` = ? LIMIT 1")) {
            ps.setString(1, name);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) return UUID.fromString(rs.getString("uuid"));
            }
        } catch (SQLException | IllegalArgumentException e) { /* ignorieren */ }
        return null;
    }

    public UUID findUUIDByName(String name) {
        if (!isConnected()) return null;
        try (Connection con = dataSource.getConnection();
             PreparedStatement ps = con.prepareStatement(
                "SELECT `player_uuid` FROM `CMI_users` WHERE `username` = ? LIMIT 1")) {
            ps.setString(1, name);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    String s = rs.getString("player_uuid");
                    if (s != null && !s.isEmpty()) return UUID.fromString(s);
                }
            }
        } catch (SQLException | IllegalArgumentException e) { /* CMI nicht vorhanden – kein Problem */ }
        return null;
    }
}
