package com.livingworld.commands;

import java.util.function.Supplier;

import com.livingworld.core.LivingWorldConstants;
import com.livingworld.modules.atmosphere.AtmosphereModule;
import com.livingworld.modules.atmosphere.AtmosphereRegionData;
import com.livingworld.modules.pollution.PollutionModule;
import com.livingworld.modules.pollution.PollutionRegionData;
import com.livingworld.modules.recovery.RecoveryModule;
import com.livingworld.modules.recovery.RecoveryRegionData;
import com.livingworld.modules.recovery.SuccessionStage;
import com.livingworld.modules.soil.SoilModule;
import com.livingworld.modules.soil.SoilRegionData;
import com.livingworld.modules.vegetation.VegetationModule;
import com.livingworld.modules.vegetation.VegetationRegionData;
import com.livingworld.modules.water.WaterModule;
import com.livingworld.modules.water.WaterRegionData;
import com.livingworld.regions.Region;
import com.livingworld.regions.RegionCoordinate;
import com.livingworld.regions.RegionManager;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;

import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;

/**
 * One-command demo presets for watching ecosystem transformation.
 *
 * <pre>
 *   /lw demo degrade  — forces MATURE_FOREST then applies severe drought + dead soil
 *                       so the region visibly regresses toward BARREN.
 *                       Recommended speed: /lw speed 20
 *
 *   /lw demo recover  — resets soil and water to lush conditions so the region
 *                       advances from wherever it is back toward MATURE_FOREST.
 *                       Recommended speed: /lw speed 50
 * </pre>
 */
public final class EcoDemoCommand {

    private EcoDemoCommand() {}

    public static LiteralArgumentBuilder<CommandSourceStack> build(Supplier<RegionManager> rm) {
        return Commands.literal("demo")
                .then(Commands.literal("degrade")
                        .executes(ctx -> runDegrade(ctx.getSource(), rm.get())))
                .then(Commands.literal("recover")
                        .executes(ctx -> runRecover(ctx.getSource(), rm.get())));
    }

    // ------------------------------------------------------------------
    // Presets
    // ------------------------------------------------------------------

    private static int runDegrade(CommandSourceStack src, RegionManager rm) {
        return applyPreset(src, rm, "DEGRADE", region -> {
            // Force starting point: peak forest.
            region.getModuleData().put(RecoveryModule.MODULE_ID,
                    new RecoveryRegionData(SuccessionStage.MATURE_FOREST, 0.0, 0.0,
                            SuccessionStage.MATURE_FOREST));

            // Lush vegetation so it visibly dies rather than starting at zero.
            region.getModuleData().put(VegetationModule.MODULE_ID,
                    new VegetationRegionData(100.0, 80.0, 80.0, 70.0, 0.0));

            // Dead soil: near-zero fertility, high erosion and contamination.
            // soilQuality ≈ 5 − 8×0.4 − 8×0.3 = 5 − 3.2 − 2.4 = −0.6 → 0
            // That floor triggers badConditions in VegetationModule and
            // conditionsMissedForRegression in RecoveryModule every cycle.
            SoilRegionData soil = SoilRegionData.defaults();
            soil.setFertility(5.0);
            soil.setMoisture(5.0);
            soil.setContamination(8.0);
            soil.setErosion(8.0);
            region.getModuleData().put(SoilModule.MODULE_ID, soil);

            // Severe drought — water availability near zero.
            WaterRegionData water = WaterRegionData.defaults();
            water.setWaterAvailability(5.0);
            water.setDroughtRisk(90.0);
            water.setPurificationCapacity(5.0);
            region.getModuleData().put(WaterModule.MODULE_ID, water);

            // Clear pollution so it doesn't confuse the read-out (soil drives the decay here).
            region.getModuleData().put(PollutionModule.MODULE_ID, PollutionRegionData.defaults());

            // Dry sky: low rain so per-player weather reflects the drought.
            AtmosphereRegionData atm = new AtmosphereRegionData(0.05, 0.0);
            region.getModuleData().put(AtmosphereModule.MODULE_ID, atm);
        }, "Stage forced to MATURE_FOREST | Soil destroyed | Drought=90 | Use /lw speed 20 to watch");
    }

    private static int runRecover(CommandSourceStack src, RegionManager rm) {
        return applyPreset(src, rm, "RECOVER", region -> {
            // Raise cap — region must be allowed to reach MATURE_FOREST again.
            RecoveryRegionData rec = region.getModuleData()
                    .get(RecoveryModule.MODULE_ID, RecoveryRegionData.class)
                    .orElseGet(RecoveryRegionData::defaults);
            rec.setMaxSuccessionStage(SuccessionStage.MATURE_FOREST);
            region.getModuleData().put(RecoveryModule.MODULE_ID, rec);

            // Rich, clean soil — contamination and erosion both zeroed.
            SoilRegionData soil = SoilRegionData.defaults();
            soil.setFertility(85.0);
            soil.setMoisture(75.0);
            soil.setContamination(0.0);
            soil.setErosion(0.0);
            region.getModuleData().put(SoilModule.MODULE_ID, soil);

            // Plentiful water, no drought.
            WaterRegionData water = WaterRegionData.defaults();
            water.setWaterAvailability(85.0);
            water.setDroughtRisk(5.0);
            water.setPurificationCapacity(80.0);
            region.getModuleData().put(WaterModule.MODULE_ID, water);

            // Clean air.
            region.getModuleData().put(PollutionModule.MODULE_ID, PollutionRegionData.defaults());

            // Good rain signal in the atmosphere.
            AtmosphereRegionData atm = new AtmosphereRegionData(0.75, 0.0);
            region.getModuleData().put(AtmosphereModule.MODULE_ID, atm);

            // Seed minimal grass so vegetation succession has something to start from.
            // (Shrubs and trees grow naturally from here.)
            VegetationRegionData veg = region.getModuleData()
                    .get(VegetationModule.MODULE_ID, VegetationRegionData.class)
                    .orElseGet(VegetationRegionData::defaults);
            veg.setGrassPressure(Math.max(veg.getGrassPressure(), 5.0));
            veg.setDeadVegetation(0.0);
            region.getModuleData().put(VegetationModule.MODULE_ID, veg);
        }, "Cap raised to MATURE_FOREST | Soil+water restored | Use /lw speed 50 to watch");
    }

    // ------------------------------------------------------------------

    private static int applyPreset(CommandSourceStack src, RegionManager rm,
                                   String label, RegionMutation mutation, String hint) {
        if (rm == null) { src.sendFailure(Component.literal("Region manager not ready.")); return 0; }
        ServerPlayer player;
        try { player = src.getPlayerOrException(); }
        catch (CommandSyntaxException e) {
            src.sendFailure(Component.literal("/lw demo must be run by a player."));
            return 0;
        }

        String dimId = player.level().dimension().location().toString();
        int rx = (int) Math.floor(player.getX() / (LivingWorldConstants.DEFAULT_REGION_SIZE_CHUNKS * 16.0));
        int rz = (int) Math.floor(player.getZ() / (LivingWorldConstants.DEFAULT_REGION_SIZE_CHUNKS * 16.0));
        RegionCoordinate coord = new RegionCoordinate(dimId, rx, rz);

        Region region = rm.getOrCreateRegion(coord);
        mutation.apply(region);
        rm.markDirty(region);

        src.sendSuccess(() -> Component.literal(
                "[LW] Demo:" + label + " applied to (" + rx + "," + rz + ") — " + hint), false);
        return 1;
    }

    @FunctionalInterface
    private interface RegionMutation { void apply(Region region); }
}
