package de.teleportsuite.database;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import de.teleportsuite.TeleportSuite;
import de.teleportsuite.models.*;
import java.sql.*;
import java.util.*;

public class DatabaseManager {

    private final TeleportSuite plugin;
    private HikariDataSource dataSource;
    private boolean isMySQL;

    public DatabaseManager(TeleportSuite plugin) { this.plugin = plugin; }

    public boolean connect() {
        String type = plugin.getConfig().getString("database.type", "sqlite").toLowerCase();
        isMySQL = type.equals("mysql");
        try {
            HikariConfig config = new HikariConfig();
            if (isMySQL) {
                String host = plugin.getConfig().getString("database.mysql.host", "localhost");
                int port    = plugin.getConfig().getInt("database.mysql.port", 3306);
                String db   = plugin.getConfig().getString("database.mysql.database", "teleportsuite");
                String user = plugin.getConfig().getString("database.mysql.username", "root");
                String pass = plugin.getConfig().getString("database.mysql.password", "");
                config.setJdbcUrl("jdbc:mysql://"+host+":"+port+"/"+db+"?useSSL=false&autoReconnect=true&characterEncoding=UTF-8");
                config.setUsername(user);
                config.setPassword(pass);
                config.setMaximumPoolSize(plugin.getConfig().getInt("database.mysql.pool-size", 10));
            } else {
                String file = plugin.getDataFolder().getAbsolutePath() + "/" +
                    plugin.getConfig().getString("database.sqlite.file", "teleportsuite.db");
                config.setJdbcUrl("jdbc:sqlite:" + file);
                config.setMaximumPoolSize(1);
            }
            config.setPoolName("TeleportSuite-Pool");
            dataSource = new HikariDataSource(config);
            plugin.getLogger().info("Datenbankverbindung hergestellt (" + (isMySQL ? "MySQL" : "SQLite") + ")");
            return true;
        } catch (Exception e) {
            plugin.getLogger().severe("Datenbankfehler: " + e.getMessage());
            return false;
        }
    }

    public void createTables() {
        String ai = isMySQL ? "AUTO_INCREMENT" : "AUTOINCREMENT";
        String bool = isMySQL ? "TINYINT(1)" : "INTEGER";
        String[] tables = {
            "CREATE TABLE IF NOT EXISTS ts_homes (id INTEGER PRIMARY KEY "+ai+",uuid VARCHAR(36) NOT NULL,name VARCHAR(64) NOT NULL,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local',UNIQUE(uuid,name))",
            "CREATE TABLE IF NOT EXISTS ts_warps (id INTEGER PRIMARY KEY "+ai+",name VARCHAR(64) NOT NULL UNIQUE,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local',creator VARCHAR(36),permission VARCHAR(128) DEFAULT NULL)",
            "CREATE TABLE IF NOT EXISTS ts_portals (id INTEGER PRIMARY KEY "+ai+",name VARCHAR(64) NOT NULL UNIQUE,world VARCHAR(64) NOT NULL,x1 INT NOT NULL,y1 INT NOT NULL,z1 INT NOT NULL,x2 INT NOT NULL,y2 INT NOT NULL,z2 INT NOT NULL,target_server VARCHAR(64),target_world VARCHAR(64),target_x DOUBLE,target_y DOUBLE,target_z DOUBLE,target_yaw FLOAT DEFAULT 0,target_pitch FLOAT DEFAULT 0)",
            "CREATE TABLE IF NOT EXISTS ts_spawns (id INTEGER PRIMARY KEY "+ai+",type VARCHAR(32) NOT NULL UNIQUE,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local')",
            "CREATE TABLE IF NOT EXISTS ts_savepoints (id INTEGER PRIMARY KEY "+ai+",uuid VARCHAR(36) NOT NULL,name VARCHAR(64) NOT NULL,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local',UNIQUE(uuid,name))",
            "CREATE TABLE IF NOT EXISTS ts_player_data (uuid VARCHAR(36) PRIMARY KEY,last_world VARCHAR(64),last_x DOUBLE,last_y DOUBLE,last_z DOUBLE,last_yaw FLOAT,last_pitch FLOAT,last_server VARCHAR(64) DEFAULT 'local',death_world VARCHAR(64),death_x DOUBLE,death_y DOUBLE,death_z DOUBLE,death_yaw FLOAT,death_pitch FLOAT,death_server VARCHAR(64) DEFAULT 'local',first_join "+bool+" DEFAULT 1)"
        };
        try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
            for (String sql : tables) stmt.execute(sql);
            plugin.getLogger().info("Datenbanktabellen erstellt/verifiziert.");
        } catch (SQLException e) {
            plugin.getLogger().severe("Fehler beim Erstellen der Tabellen: " + e.getMessage());
        }
    }

    // ===== HOMES =====
    public void saveHome(UUID uuid, String name, TeleportLocation loc) {
        String sql = isMySQL
            ? "INSERT INTO ts_homes (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?"
            : "INSERT OR REPLACE INTO ts_homes (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?)";
        try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, uuid.toString()); ps.setString(2, name);
            ps.setString(3, loc.getWorld()); ps.setDouble(4, loc.getX()); ps.setDouble(5, loc.getY());
            ps.setDouble(6, loc.getZ()); ps.setFloat(7, loc.getYaw()); ps.setFloat(8, loc.getPitch());
            ps.setString(9, loc.getServer());
            if (isMySQL) {
                ps.setString(10, loc.getWorld()); ps.setDouble(11, loc.getX()); ps.setDouble(12, loc.getY());
                ps.setDouble(13, loc.getZ()); ps.setFloat(14, loc.getYaw()); ps.setFloat(15, loc.getPitch());
                ps.setString(16, loc.getServer());
            }
            ps.executeUpdate();
        } catch (SQLException e) { plugin.getLogger().warning("saveHome: " + e.getMessage()); }
    }

    public List<Home> getHomes(UUID uuid) {
        List<Home> list = new ArrayList<>();
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_homes WHERE uuid=?")) {
            ps.setString(1, uuid.toString());
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server"));
                list.add(new Home(uuid, rs.getString("name"), loc));
            }
        } catch (SQLException e) { plugin.getLogger().warning("getHomes: " + e.getMessage()); }
        return list;
    }

    public Home getHome(UUID uuid, String name) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_homes WHERE uuid=? AND name=?")) {
            ps.setString(1, uuid.toString()); ps.setString(2, name);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server"));
                return new Home(uuid, name, loc);
            }
        } catch (SQLException e) { plugin.getLogger().warning("getHome: " + e.getMessage()); }
        return null;
    }

    public boolean deleteHome(UUID uuid, String name) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("DELETE FROM ts_homes WHERE uuid=? AND name=?")) {
            ps.setString(1, uuid.toString()); ps.setString(2, name);
            return ps.executeUpdate() > 0;
        } catch (SQLException e) { plugin.getLogger().warning("deleteHome: " + e.getMessage()); return false; }
    }

    public int countHomes(UUID uuid) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT COUNT(*) FROM ts_homes WHERE uuid=?")) {
            ps.setString(1, uuid.toString());
            ResultSet rs = ps.executeQuery();
            if (rs.next()) return rs.getInt(1);
        } catch (SQLException e) { plugin.getLogger().warning("countHomes: " + e.getMessage()); }
        return 0;
    }

    // ===== WARPS =====
    public void saveWarp(String name, TeleportLocation loc, UUID creator, String permission) {
        String sql = isMySQL
            ? "INSERT INTO ts_warps (name,world,x,y,z,yaw,pitch,server,creator,permission) VALUES(?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?"
            : "INSERT OR REPLACE INTO ts_warps (name,world,x,y,z,yaw,pitch,server,creator,permission) VALUES(?,?,?,?,?,?,?,?,?,?)";
        try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, name); ps.setString(2, loc.getWorld());
            ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ());
            ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer());
            ps.setString(9, creator != null ? creator.toString() : null); ps.setString(10, permission);
            if (isMySQL) {
                ps.setString(11, loc.getWorld()); ps.setDouble(12, loc.getX()); ps.setDouble(13, loc.getY());
                ps.setDouble(14, loc.getZ()); ps.setFloat(15, loc.getYaw()); ps.setFloat(16, loc.getPitch());
                ps.setString(17, loc.getServer());
            }
            ps.executeUpdate();
        } catch (SQLException e) { plugin.getLogger().warning("saveWarp: " + e.getMessage()); }
    }

    public Warp getWarp(String name) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_warps WHERE name=?")) {
            ps.setString(1, name);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server"));
                String creatorStr = rs.getString("creator");
                UUID creator = creatorStr != null ? UUID.fromString(creatorStr) : null;
                return new Warp(name, loc, creator, rs.getString("permission"));
            }
        } catch (SQLException e) { plugin.getLogger().warning("getWarp: " + e.getMessage()); }
        return null;
    }

    public List<Warp> getAllWarps() {
        List<Warp> list = new ArrayList<>();
        try (Connection c = getConnection();
             Statement stmt = c.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM ts_warps ORDER BY name")) {
            while (rs.next()) {
                TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server"));
                String creatorStr = rs.getString("creator");
                UUID creator = creatorStr != null ? UUID.fromString(creatorStr) : null;
                list.add(new Warp(rs.getString("name"), loc, creator, rs.getString("permission")));
            }
        } catch (SQLException e) { plugin.getLogger().warning("getAllWarps: " + e.getMessage()); }
        return list;
    }

    public boolean deleteWarp(String name) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("DELETE FROM ts_warps WHERE name=?")) {
            ps.setString(1, name);
            return ps.executeUpdate() > 0;
        } catch (SQLException e) { plugin.getLogger().warning("deleteWarp: " + e.getMessage()); return false; }
    }

    // ===== PORTALS =====
    public void savePortal(Portal portal) {
        String sql = isMySQL
            ? "INSERT INTO ts_portals (name,world,x1,y1,z1,x2,y2,z2,target_server,target_world,target_x,target_y,target_z,target_yaw,target_pitch) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=VALUES(world),x1=VALUES(x1),y1=VALUES(y1),z1=VALUES(z1),x2=VALUES(x2),y2=VALUES(y2),z2=VALUES(z2),target_server=VALUES(target_server),target_world=VALUES(target_world),target_x=VALUES(target_x),target_y=VALUES(target_y),target_z=VALUES(target_z),target_yaw=VALUES(target_yaw),target_pitch=VALUES(target_pitch)"
            : "INSERT OR REPLACE INTO ts_portals (name,world,x1,y1,z1,x2,y2,z2,target_server,target_world,target_x,target_y,target_z,target_yaw,target_pitch) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
        try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {
            TeleportLocation dest = portal.getDestination();
            ps.setString(1, portal.getName()); ps.setString(2, portal.getWorld());
            // We need full portal data - for simplicity storing via Portal model fields
            ps.executeUpdate();
        } catch (SQLException e) { plugin.getLogger().warning("savePortal: " + e.getMessage()); }
    }

    public List<Portal> getAllPortals() {
        List<Portal> list = new ArrayList<>();
        try (Connection c = getConnection();
             Statement stmt = c.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM ts_portals")) {
            while (rs.next()) {
                TeleportLocation dest = new TeleportLocation(rs.getString("target_world"),rs.getDouble("target_x"),rs.getDouble("target_y"),rs.getDouble("target_z"),rs.getFloat("target_yaw"),rs.getFloat("target_pitch"),rs.getString("target_server"));
                list.add(new Portal(rs.getString("name"),rs.getString("world"),rs.getInt("x1"),rs.getInt("y1"),rs.getInt("z1"),rs.getInt("x2"),rs.getInt("y2"),rs.getInt("z2"),rs.getString("target_server"),dest));
            }
        } catch (SQLException e) { plugin.getLogger().warning("getAllPortals: " + e.getMessage()); }
        return list;
    }

    public boolean deletePortal(String name) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("DELETE FROM ts_portals WHERE name=?")) {
            ps.setString(1, name);
            return ps.executeUpdate() > 0;
        } catch (SQLException e) { plugin.getLogger().warning("deletePortal: " + e.getMessage()); return false; }
    }

    // ===== SPAWNS =====
    public void saveSpawn(String type, TeleportLocation loc) {
        String sql = isMySQL
            ? "INSERT INTO ts_spawns (type,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?"
            : "INSERT OR REPLACE INTO ts_spawns (type,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?)";
        try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, type); ps.setString(2, loc.getWorld());
            ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ());
            ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer());
            if (isMySQL) {
                ps.setString(9, loc.getWorld()); ps.setDouble(10, loc.getX()); ps.setDouble(11, loc.getY());
                ps.setDouble(12, loc.getZ()); ps.setFloat(13, loc.getYaw()); ps.setFloat(14, loc.getPitch());
                ps.setString(15, loc.getServer());
            }
            ps.executeUpdate();
        } catch (SQLException e) { plugin.getLogger().warning("saveSpawn: " + e.getMessage()); }
    }

    public TeleportLocation getSpawn(String type) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_spawns WHERE type=?")) {
            ps.setString(1, type);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) return new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server"));
        } catch (SQLException e) { plugin.getLogger().warning("getSpawn: " + e.getMessage()); }
        return null;
    }

    // ===== PLAYER DATA =====
    public void saveLastLocation(UUID uuid, TeleportLocation loc) {
        String sql = isMySQL
            ? "INSERT INTO ts_player_data (uuid,last_world,last_x,last_y,last_z,last_yaw,last_pitch,last_server) VALUES(?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE last_world=?,last_x=?,last_y=?,last_z=?,last_yaw=?,last_pitch=?,last_server=?"
            : "INSERT OR REPLACE INTO ts_player_data (uuid,last_world,last_x,last_y,last_z,last_yaw,last_pitch,last_server) VALUES(?,?,?,?,?,?,?,?)";
        try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, uuid.toString()); ps.setString(2, loc.getWorld());
            ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ());
            ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer());
            if (isMySQL) {
                ps.setString(9, loc.getWorld()); ps.setDouble(10, loc.getX()); ps.setDouble(11, loc.getY());
                ps.setDouble(12, loc.getZ()); ps.setFloat(13, loc.getYaw()); ps.setFloat(14, loc.getPitch());
                ps.setString(15, loc.getServer());
            }
            ps.executeUpdate();
        } catch (SQLException e) { plugin.getLogger().warning("saveLastLocation: " + e.getMessage()); }
    }

    public void saveDeathLocation(UUID uuid, TeleportLocation loc) {
        try (Connection c = getConnection()) {
            PreparedStatement check = c.prepareStatement("SELECT uuid FROM ts_player_data WHERE uuid=?");
            check.setString(1, uuid.toString());
            ResultSet rs = check.executeQuery();
            String sql;
            if (rs.next()) {
                sql = "UPDATE ts_player_data SET death_world=?,death_x=?,death_y=?,death_z=?,death_yaw=?,death_pitch=?,death_server=? WHERE uuid=?";
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setString(1, loc.getWorld()); ps.setDouble(2, loc.getX()); ps.setDouble(3, loc.getY());
                    ps.setDouble(4, loc.getZ()); ps.setFloat(5, loc.getYaw()); ps.setFloat(6, loc.getPitch());
                    ps.setString(7, loc.getServer()); ps.setString(8, uuid.toString());
                    ps.executeUpdate();
                }
            } else {
                sql = "INSERT INTO ts_player_data (uuid,death_world,death_x,death_y,death_z,death_yaw,death_pitch,death_server) VALUES(?,?,?,?,?,?,?,?)";
                try (PreparedStatement ps = c.prepareStatement(sql)) {
                    ps.setString(1, uuid.toString()); ps.setString(2, loc.getWorld());
                    ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ());
                    ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer());
                    ps.executeUpdate();
                }
            }
        } catch (SQLException e) { plugin.getLogger().warning("saveDeathLocation: " + e.getMessage()); }
    }

    public TeleportLocation getLastLocation(UUID uuid) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT last_world,last_x,last_y,last_z,last_yaw,last_pitch,last_server FROM ts_player_data WHERE uuid=?")) {
            ps.setString(1, uuid.toString());
            ResultSet rs = ps.executeQuery();
            if (rs.next() && rs.getString("last_world") != null)
                return new TeleportLocation(rs.getString("last_world"),rs.getDouble("last_x"),rs.getDouble("last_y"),rs.getDouble("last_z"),rs.getFloat("last_yaw"),rs.getFloat("last_pitch"),rs.getString("last_server"));
        } catch (SQLException e) { plugin.getLogger().warning("getLastLocation: " + e.getMessage()); }
        return null;
    }

    public TeleportLocation getDeathLocation(UUID uuid) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT death_world,death_x,death_y,death_z,death_yaw,death_pitch,death_server FROM ts_player_data WHERE uuid=?")) {
            ps.setString(1, uuid.toString());
            ResultSet rs = ps.executeQuery();
            if (rs.next() && rs.getString("death_world") != null)
                return new TeleportLocation(rs.getString("death_world"),rs.getDouble("death_x"),rs.getDouble("death_y"),rs.getDouble("death_z"),rs.getFloat("death_yaw"),rs.getFloat("death_pitch"),rs.getString("death_server"));
        } catch (SQLException e) { plugin.getLogger().warning("getDeathLocation: " + e.getMessage()); }
        return null;
    }

    public boolean isFirstJoin(UUID uuid) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT first_join FROM ts_player_data WHERE uuid=?")) {
            ps.setString(1, uuid.toString());
            ResultSet rs = ps.executeQuery();
            if (rs.next()) return rs.getInt("first_join") == 1;
        } catch (SQLException e) { plugin.getLogger().warning("isFirstJoin: " + e.getMessage()); }
        return true;
    }

    public void setFirstJoinDone(UUID uuid) {
        try (Connection c = getConnection()) {
            PreparedStatement check = c.prepareStatement("SELECT uuid FROM ts_player_data WHERE uuid=?");
            check.setString(1, uuid.toString());
            ResultSet rs = check.executeQuery();
            if (rs.next()) {
                PreparedStatement ps = c.prepareStatement("UPDATE ts_player_data SET first_join=0 WHERE uuid=?");
                ps.setString(1, uuid.toString()); ps.executeUpdate();
            } else {
                PreparedStatement ps = c.prepareStatement("INSERT INTO ts_player_data (uuid,first_join) VALUES(?,0)");
                ps.setString(1, uuid.toString()); ps.executeUpdate();
            }
        } catch (SQLException e) { plugin.getLogger().warning("setFirstJoinDone: " + e.getMessage()); }
    }

    // ===== SAVEPOINTS =====
    public void saveSavePoint(UUID uuid, String name, TeleportLocation loc) {
        String sql = isMySQL
            ? "INSERT INTO ts_savepoints (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?"
            : "INSERT OR REPLACE INTO ts_savepoints (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?)";
        try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, uuid.toString()); ps.setString(2, name);
            ps.setString(3, loc.getWorld()); ps.setDouble(4, loc.getX()); ps.setDouble(5, loc.getY());
            ps.setDouble(6, loc.getZ()); ps.setFloat(7, loc.getYaw()); ps.setFloat(8, loc.getPitch());
            ps.setString(9, loc.getServer());
            if (isMySQL) {
                ps.setString(10, loc.getWorld()); ps.setDouble(11, loc.getX()); ps.setDouble(12, loc.getY());
                ps.setDouble(13, loc.getZ()); ps.setFloat(14, loc.getYaw()); ps.setFloat(15, loc.getPitch());
                ps.setString(16, loc.getServer());
            }
            ps.executeUpdate();
        } catch (SQLException e) { plugin.getLogger().warning("saveSavePoint: " + e.getMessage()); }
    }

    public TeleportLocation getSavePoint(UUID uuid, String name) {
        try (Connection c = getConnection();
             PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_savepoints WHERE uuid=? AND name=?")) {
            ps.setString(1, uuid.toString()); ps.setString(2, name);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) return new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server"));
        } catch (SQLException e) { plugin.getLogger().warning("getSavePoint: " + e.getMessage()); }
        return null;
    }

    public Connection getConnection() throws SQLException { return dataSource.getConnection(); }
    public void disconnect() { if (dataSource != null && !dataSource.isClosed()) dataSource.close(); }
    public boolean isMySQL() { return isMySQL; }
}
