package com.spaceshipproject;

import org.bukkit.entity.Player;

import java.io.File;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Gestionnaire de la base de données SQLite.
 *
 * Tables :
 *   - players(uuid, name, settings, created_at, updated_at)
 *   - spaceships(id, owner_uuid, name, size_x/y/z, spawn_x/y/z, schematic, settings, created_at, updated_at)
 *   - spaceships_loaded(spaceship_id, world, origin_x/y/z, loaded_at)
 */
public class DatabaseManager {

    private final SpaceShipProject plugin;
    private Connection connection;

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

    public void initialize() {
        try {
            File dataFolder = plugin.getDataFolder();
            if (!dataFolder.exists()) {
                dataFolder.mkdirs();
            }
            File dbFile = new File(dataFolder, "spaceships.db");
            connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath());
            try (Statement stmt = connection.createStatement()) {
                stmt.execute("PRAGMA foreign_keys = ON;");
            }
            createTables();
            plugin.getLogger().info("Base de donnees SpaceShipProject initialisee.");
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur a l'initialisation de la base : " + e.getMessage());
        }
    }

    public void close() {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur a la fermeture de la base : " + e.getMessage());
        }
    }

    private void createTables() throws SQLException {
        String players = """
            CREATE TABLE IF NOT EXISTS players (
                uuid TEXT PRIMARY KEY,
                name TEXT,
                settings TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """;
        String spaceships = """
            CREATE TABLE IF NOT EXISTS spaceships (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                owner_uuid TEXT NOT NULL,
                name TEXT NOT NULL,
                size_x INTEGER NOT NULL,
                size_y INTEGER NOT NULL,
                size_z INTEGER NOT NULL,
                spawn_x INTEGER NOT NULL,
                spawn_y INTEGER NOT NULL,
                spawn_z INTEGER NOT NULL,
                schematic TEXT,
                settings TEXT,
                level INTEGER NOT NULL DEFAULT 1,
                money INTEGER NOT NULL DEFAULT 0,
                last_loaded_level INTEGER NOT NULL DEFAULT 1,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (owner_uuid) REFERENCES players(uuid) ON DELETE CASCADE
            )
        """;
        String loaded = """
            CREATE TABLE IF NOT EXISTS spaceships_loaded (
                spaceship_id INTEGER PRIMARY KEY,
                world TEXT NOT NULL,
                origin_x INTEGER NOT NULL,
                origin_y INTEGER NOT NULL,
                origin_z INTEGER NOT NULL,
                loaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (spaceship_id) REFERENCES spaceships(id) ON DELETE CASCADE
            )
        """;
        try (Statement stmt = connection.createStatement()) {
            stmt.execute(players);
            stmt.execute(spaceships);
            stmt.execute(loaded);
            // Migrations (idempotentes) pour les bases creees avant l'ajout de level/money.
            tryAlter(stmt, "ALTER TABLE spaceships ADD COLUMN level INTEGER NOT NULL DEFAULT 1");
            tryAlter(stmt, "ALTER TABLE spaceships ADD COLUMN money INTEGER NOT NULL DEFAULT 0");
            tryAlter(stmt, "ALTER TABLE spaceships ADD COLUMN last_loaded_level INTEGER NOT NULL DEFAULT 1");
        }
    }

    private void tryAlter(Statement stmt, String sql) {
        try { stmt.execute(sql); } catch (SQLException ignored) { /* colonne deja presente */ }
    }

    /* ========================= PLAYERS ========================= */

    public void upsertPlayer(Player player) {
        String sql = """
            INSERT INTO players(uuid, name, updated_at)
            VALUES(?, ?, CURRENT_TIMESTAMP)
            ON CONFLICT(uuid) DO UPDATE SET name = excluded.name, updated_at = CURRENT_TIMESTAMP
        """;
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, player.getUniqueId().toString());
            stmt.setString(2, player.getName());
            stmt.executeUpdate();
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur upsertPlayer : " + e.getMessage());
        }
    }

    public void setPlayerSettings(UUID uuid, String settingsJson) {
        String sql = "UPDATE players SET settings = ?, updated_at = CURRENT_TIMESTAMP WHERE uuid = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, settingsJson);
            stmt.setString(2, uuid.toString());
            stmt.executeUpdate();
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur setPlayerSettings : " + e.getMessage());
        }
    }

    public String getPlayerSettings(UUID uuid) {
        String sql = "SELECT settings FROM players WHERE uuid = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, uuid.toString());
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) return rs.getString("settings");
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur getPlayerSettings : " + e.getMessage());
        }
        return null;
    }

    /* ========================= SPACESHIPS ========================= */

    public long createSpaceship(UUID owner, String name,
                                int sizeX, int sizeY, int sizeZ,
                                int spawnX, int spawnY, int spawnZ,
                                String schematicJson, String settingsJson) {
        String sql = """
            INSERT INTO spaceships(owner_uuid, name, size_x, size_y, size_z,
                                   spawn_x, spawn_y, spawn_z, schematic, settings, level, money, last_loaded_level)
            VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, 1)
        """;
        try (PreparedStatement stmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            stmt.setString(1, owner.toString());
            stmt.setString(2, name);
            stmt.setInt(3, sizeX);
            stmt.setInt(4, sizeY);
            stmt.setInt(5, sizeZ);
            stmt.setInt(6, spawnX);
            stmt.setInt(7, spawnY);
            stmt.setInt(8, spawnZ);
            stmt.setString(9, schematicJson);
            stmt.setString(10, settingsJson);
            stmt.executeUpdate();
            try (ResultSet keys = stmt.getGeneratedKeys()) {
                if (keys.next()) return keys.getLong(1);
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur createSpaceship : " + e.getMessage());
        }
        return -1;
    }

    public Spaceship getSpaceship(long id) {
        String sql = "SELECT * FROM spaceships WHERE id = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setLong(1, id);
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) return rowToSpaceship(rs);
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur getSpaceship : " + e.getMessage());
        }
        return null;
    }

    public List<Spaceship> getSpaceshipsByOwner(UUID owner) {
        List<Spaceship> list = new ArrayList<>();
        String sql = "SELECT * FROM spaceships WHERE owner_uuid = ? ORDER BY id ASC";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, owner.toString());
            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) list.add(rowToSpaceship(rs));
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur getSpaceshipsByOwner : " + e.getMessage());
        }
        return list;
    }

    public int countSpaceshipsByOwner(UUID owner) {
        String sql = "SELECT COUNT(*) FROM spaceships WHERE owner_uuid = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, owner.toString());
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) return rs.getInt(1);
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur countSpaceshipsByOwner : " + e.getMessage());
        }
        return 0;
    }

    @SuppressWarnings("deprecation")
    public void updateSpaceship(Spaceship ship) {
        String sql = """
            UPDATE spaceships SET
                name = ?, size_x = ?, size_y = ?, size_z = ?,
                spawn_x = ?, spawn_y = ?, spawn_z = ?,
                schematic = ?, settings = ?, level = ?, money = ?, last_loaded_level = ?,
                updated_at = CURRENT_TIMESTAMP
            WHERE id = ?
        """;
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, ship.getName());
            stmt.setInt(2, ship.getSizeX());
            stmt.setInt(3, ship.getSizeY());
            stmt.setInt(4, ship.getSizeZ());
            stmt.setInt(5, ship.getSpawnX());
            stmt.setInt(6, ship.getSpawnY());
            stmt.setInt(7, ship.getSpawnZ());
            stmt.setString(8, ship.getSchematicJson());
            stmt.setString(9, ship.getSettingsJson());
            stmt.setInt(10, ship.getLevel());
            stmt.setInt(11, ship.getMoney());
            stmt.setInt(12, ship.getLastLoadedLevel());
            stmt.setLong(13, ship.getId());
            stmt.executeUpdate();
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur updateSpaceship : " + e.getMessage());
        }
    }

    public void deleteSpaceship(long id) {
        try (PreparedStatement st1 = connection.prepareStatement("DELETE FROM spaceships_loaded WHERE spaceship_id = ?");
             PreparedStatement st2 = connection.prepareStatement("DELETE FROM spaceships WHERE id = ?")) {
            st1.setLong(1, id);
            st1.executeUpdate();
            st2.setLong(1, id);
            st2.executeUpdate();
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur deleteSpaceship : " + e.getMessage());
        }
    }

    private Spaceship rowToSpaceship(ResultSet rs) throws SQLException {
        int level = 1, money = 0, lastLoadedLevel = 1;
        try { level = rs.getInt("level"); if (rs.wasNull()) level = 1; } catch (SQLException ignored) {}
        try { money = rs.getInt("money"); } catch (SQLException ignored) {}
        try { lastLoadedLevel = rs.getInt("last_loaded_level"); if (rs.wasNull()) lastLoadedLevel = 1; } catch (SQLException ignored) {}
        return new Spaceship(
                rs.getLong("id"),
                UUID.fromString(rs.getString("owner_uuid")),
                rs.getString("name"),
                rs.getInt("size_x"), rs.getInt("size_y"), rs.getInt("size_z"),
                rs.getInt("spawn_x"), rs.getInt("spawn_y"), rs.getInt("spawn_z"),
                rs.getString("schematic"),
                rs.getString("settings"),
                level, money, lastLoadedLevel
        );
    }

    /* ========================= SPACESHIPS_LOADED ========================= */

    public void markLoaded(long spaceshipId, String world, int x, int y, int z) {
        String sql = """
            INSERT INTO spaceships_loaded(spaceship_id, world, origin_x, origin_y, origin_z, loaded_at)
            VALUES(?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
            ON CONFLICT(spaceship_id) DO UPDATE SET
                world = excluded.world,
                origin_x = excluded.origin_x,
                origin_y = excluded.origin_y,
                origin_z = excluded.origin_z,
                loaded_at = CURRENT_TIMESTAMP
        """;
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setLong(1, spaceshipId);
            stmt.setString(2, world);
            stmt.setInt(3, x);
            stmt.setInt(4, y);
            stmt.setInt(5, z);
            stmt.executeUpdate();
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur markLoaded : " + e.getMessage());
        }
    }

    public void markUnloaded(long spaceshipId) {
        String sql = "DELETE FROM spaceships_loaded WHERE spaceship_id = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setLong(1, spaceshipId);
            stmt.executeUpdate();
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur markUnloaded : " + e.getMessage());
        }
    }

    public LoadedSpaceship getLoaded(long spaceshipId) {
        String sql = """
            SELECT l.spaceship_id, l.world, l.origin_x, l.origin_y, l.origin_z,
                   s.size_x, s.size_y, s.size_z
            FROM spaceships_loaded l
            JOIN spaceships s ON s.id = l.spaceship_id
            WHERE l.spaceship_id = ?
        """;
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setLong(1, spaceshipId);
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    return new LoadedSpaceship(
                            rs.getLong("spaceship_id"),
                            rs.getString("world"),
                            rs.getInt("origin_x"), rs.getInt("origin_y"), rs.getInt("origin_z"),
                            rs.getInt("size_x"), rs.getInt("size_y"), rs.getInt("size_z")
                    );
                }
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur getLoaded : " + e.getMessage());
        }
        return null;
    }

    public List<LoadedSpaceship> getLoadedByOwner(UUID owner) {
        List<LoadedSpaceship> list = new ArrayList<>();
        String sql = """
            SELECT l.spaceship_id, l.world, l.origin_x, l.origin_y, l.origin_z,
                   s.size_x, s.size_y, s.size_z
            FROM spaceships_loaded l
            JOIN spaceships s ON s.id = l.spaceship_id
            WHERE s.owner_uuid = ?
        """;
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, owner.toString());
            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    list.add(new LoadedSpaceship(
                            rs.getLong("spaceship_id"),
                            rs.getString("world"),
                            rs.getInt("origin_x"), rs.getInt("origin_y"), rs.getInt("origin_z"),
                            rs.getInt("size_x"), rs.getInt("size_y"), rs.getInt("size_z")
                    ));
                }
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur getLoadedByOwner : " + e.getMessage());
        }
        return list;
    }

    public List<LoadedSpaceship> getAllLoaded() {
        List<LoadedSpaceship> list = new ArrayList<>();
        String sql = """
            SELECT l.spaceship_id, l.world, l.origin_x, l.origin_y, l.origin_z,
                   s.size_x, s.size_y, s.size_z
            FROM spaceships_loaded l
            JOIN spaceships s ON s.id = l.spaceship_id
        """;
        try (PreparedStatement stmt = connection.prepareStatement(sql);
             ResultSet rs = stmt.executeQuery()) {
            while (rs.next()) {
                list.add(new LoadedSpaceship(
                        rs.getLong("spaceship_id"),
                        rs.getString("world"),
                        rs.getInt("origin_x"), rs.getInt("origin_y"), rs.getInt("origin_z"),
                        rs.getInt("size_x"), rs.getInt("size_y"), rs.getInt("size_z")
                ));
            }
        } catch (SQLException e) {
            plugin.getLogger().severe("Erreur getAllLoaded : " + e.getMessage());
        }
        return list;
    }
}
