package com.spaceshipproject;

import org.bukkit.World;
import org.bukkit.entity.Player;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * Gere les modeles (templates) de spaceship :
 *   - lecture / ecriture des schematiques modeles sur disque (dossier "models/")
 *   - sessions d'edition par admin (loadModel / saveModel)
 *
 * Le modele "default.json" est utilise par /ss create quand il existe.
 */
public class ModelManager {

    private static final String DEFAULT = "default";

    private final SpaceShipProject plugin;
    private final File modelsDir;

    /** Sessions d'edition actives : adminUuid -> session. */
    private final Map<UUID, ModelEditSession> sessions = new HashMap<>();

    public ModelManager(SpaceShipProject plugin) {
        this.plugin = plugin;
        this.modelsDir = new File(plugin.getDataFolder(), "models");
        if (!modelsDir.exists()) modelsDir.mkdirs();
    }

    /** Recharge depuis disque (ne fait rien d'autre, lecture a la demande). */
    public void reload() { /* no-op : on lit a la demande */ }

    /** Charge un modele du disque, ou null si absent. */
    public String loadModelJson(String name) {
        File file = new File(modelsDir, sanitize(name) + ".json");
        if (!file.exists()) return null;
        try {
            return Files.readString(file.toPath(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            plugin.getLogger().warning("Lecture modele " + name + " : " + e.getMessage());
            return null;
        }
    }

    /** Ecrit un modele sur disque. */
    public boolean saveModelJson(String name, String json) {
        File file = new File(modelsDir, sanitize(name) + ".json");
        try {
            Files.writeString(file.toPath(), json, StandardCharsets.UTF_8);
            return true;
        } catch (IOException e) {
            plugin.getLogger().warning("Ecriture modele " + name + " : " + e.getMessage());
            return false;
        }
    }

    /** Charge le modele "default", ou null si absent. */
    public String loadDefaultModelJson() { return loadModelJson(DEFAULT); }

    /** True si "default.json" est present sur disque. */
    public boolean hasDefaultModel() { return loadDefaultModelJson() != null; }

    /* ============================ EDIT SESSIONS ============================ */

    /**
     * Demarre une session d'edition : place la schematique du modele dans le monde devant l'admin.
     * Retourne true si OK.
     */
    public boolean loadForEdit(Player admin, String modelName) {
        if (sessions.containsKey(admin.getUniqueId())) {
            admin.sendMessage(ConfigManager.color("&cVous avez deja une session d'edition. /ssa savemodel ou /ssa cancelmodel."));
            return false;
        }
        ConfigManager cfg = plugin.getConfigManager();
        String json = loadModelJson(modelName);
        if (json == null) {
            // Generer un modele initial a partir de la config par defaut.
            json = SchematicHelper.buildDefaultSchematic(
                    cfg.defaultSizeX(), cfg.defaultSizeY(), cfg.defaultSizeZ(),
                    cfg.defaultPlatformMaterial(),
                    cfg.defaultEdgeMaterial(),
                    cfg.defaultGlassMaterial());
        }
        int[] size = SchematicHelper.readSize(json);
        World world = admin.getWorld();
        int ox = admin.getLocation().getBlockX() - size[0] / 2;
        int oz = admin.getLocation().getBlockZ() - size[2] / 2;
        int oy = admin.getLocation().getBlockY() + cfg.summonOffsetY();
        // Empeche d'ecraser des spaceships charges.
        if (SchematicHelper.hasCollision(world, ox, oy, oz, size[0], size[1], size[2])) {
            admin.sendMessage(ConfigManager.color("&cZone obstruee, deplacez-vous puis reessayez."));
            return false;
        }
        try {
            SchematicHelper.restoreRegion(json, world, ox, oy, oz);
        } catch (Exception ex) {
            admin.sendMessage(ConfigManager.color("&cErreur restauration modele : " + ex.getMessage()));
            return false;
        }
        sessions.put(admin.getUniqueId(),
                new ModelEditSession(modelName, world.getName(), ox, oy, oz, size[0], size[1], size[2]));
        admin.sendMessage(ConfigManager.color("&aModele &b" + modelName + " &acharge en x=" + ox + " y=" + oy + " z=" + oz + ". Editez puis /ssa savemodel."));
        return true;
    }

    /** Capture la session active de l'admin et l'ecrit sur disque. */
    public boolean saveEdit(Player admin) {
        ModelEditSession s = sessions.get(admin.getUniqueId());
        if (s == null) {
            admin.sendMessage(ConfigManager.color("&cAucune session d'edition active."));
            return false;
        }
        World world = plugin.getServer().getWorld(s.worldName);
        if (world == null) {
            admin.sendMessage(ConfigManager.color("&cMonde introuvable."));
            sessions.remove(admin.getUniqueId());
            return false;
        }
        // Spawn = milieu sur le sol +1, comme le defaut, mais conserve celui du modele original si present.
        int spawnX = s.sizeX / 2, spawnY = 1, spawnZ = s.sizeZ / 2;
        String existing = loadModelJson(s.modelName);
        if (existing != null) {
            try {
                int[] sp = SchematicHelper.readSpawn(existing);
                spawnX = sp[0]; spawnY = sp[1]; spawnZ = sp[2];
            } catch (Exception ignored) {}
        }
        String json = SchematicHelper.captureRegion(world, s.originX, s.originY, s.originZ,
                s.sizeX, s.sizeY, s.sizeZ, spawnX, spawnY, spawnZ);
        boolean ok = saveModelJson(s.modelName, json);
        if (ok) {
            SchematicHelper.clearRegion(world, s.originX, s.originY, s.originZ, s.sizeX, s.sizeY, s.sizeZ);
            sessions.remove(admin.getUniqueId());
            admin.sendMessage(ConfigManager.color("&aModele &b" + s.modelName + " &asauvegarde."));
        } else {
            admin.sendMessage(ConfigManager.color("&cErreur d'ecriture du modele."));
        }
        return ok;
    }

    /** Annule la session : nettoie la zone et oublie la session. */
    public boolean cancelEdit(Player admin) {
        ModelEditSession s = sessions.remove(admin.getUniqueId());
        if (s == null) {
            admin.sendMessage(ConfigManager.color("&cAucune session active."));
            return false;
        }
        World world = plugin.getServer().getWorld(s.worldName);
        if (world != null) {
            SchematicHelper.clearRegion(world, s.originX, s.originY, s.originZ, s.sizeX, s.sizeY, s.sizeZ);
        }
        admin.sendMessage(ConfigManager.color("&7Session d'edition annulee."));
        return true;
    }

    public ModelEditSession getSession(UUID admin) { return sessions.get(admin); }

    /** Decharge toutes les sessions au shutdown (sauvegarde best-effort + nettoyage). */
    public void unloadAllOnShutdown() {
        for (Map.Entry<UUID, ModelEditSession> entry : sessions.entrySet()) {
            ModelEditSession s = entry.getValue();
            World world = plugin.getServer().getWorld(s.worldName);
            if (world != null) {
                try {
                    String json = SchematicHelper.captureRegion(world, s.originX, s.originY, s.originZ,
                            s.sizeX, s.sizeY, s.sizeZ, s.sizeX / 2, 1, s.sizeZ / 2);
                    saveModelJson(s.modelName, json);
                    SchematicHelper.clearRegion(world, s.originX, s.originY, s.originZ, s.sizeX, s.sizeY, s.sizeZ);
                } catch (Exception ex) {
                    plugin.getLogger().warning("Erreur shutdown session modele : " + ex.getMessage());
                }
            }
        }
        sessions.clear();
    }

    private String sanitize(String name) {
        if (name == null || name.isBlank()) return DEFAULT;
        return name.toLowerCase().replaceAll("[^a-z0-9_-]", "");
    }

    /** Petit conteneur immuable. */
    public static final class ModelEditSession {
        public final String modelName;
        public final String worldName;
        public final int originX, originY, originZ;
        public final int sizeX, sizeY, sizeZ;

        public ModelEditSession(String modelName, String worldName,
                                int originX, int originY, int originZ,
                                int sizeX, int sizeY, int sizeZ) {
            this.modelName = modelName;
            this.worldName = worldName;
            this.originX = originX; this.originY = originY; this.originZ = originZ;
            this.sizeX = sizeX; this.sizeY = sizeY; this.sizeZ = sizeZ;
        }
    }
}
