package com.livingworld.modules.pollution;

import com.livingworld.config.EcosystemTuning;
import com.livingworld.core.services.CoreServices;
import com.livingworld.data.serialization.PersistenceReader;
import com.livingworld.data.serialization.PersistenceWriter;
import com.livingworld.events.LivingWorldEvent;
import com.livingworld.modules.ModuleContext;
import com.livingworld.modules.ModuleMetadata;
import com.livingworld.modules.ModuleUpdateResult;
import com.livingworld.modules.RegionUpdateContext;
import com.livingworld.modules.ServerContext;
import com.livingworld.modules.SimulationModule;
import com.livingworld.regions.Region;
import com.livingworld.regions.RegionMetrics;
import java.util.List;

/**
 * Simulates natural pollution decay and computes the summary pollution metrics
 * written to {@link RegionMetrics}.
 *
 * <p>This module should run first in the ecosystem update order so that
 * {@link com.livingworld.modules.soil.SoilModule} and
 * {@link com.livingworld.modules.vegetation.VegetationModule} see current-tick
 * pollution values when they execute.
 *
 * <h3>Per-tick rules</h3>
 * <ol>
 *   <li>Air, ground, and water pollution each decay at different rates.
 *   <li>Ground pollution slowly leaches into water.
 *   <li>{@link RegionMetrics#getPollutionScore()} is recomputed as a weighted average.
 *   <li>{@link RegionMetrics#getWaterQuality()} is reduced proportionally to water pollution.
 * </ol>
 *
 * <h3>Configurable constants</h3>
 * These are intentionally simple for V1 and expected to be tuned.
 * <ul>
 *   <li>BASE_DECAY_RATE – fraction of pollution removed per tick before resistance
 *   <li>GROUND_TO_WATER_LEACH – fraction of groundPollution that leaches to water each tick
 *   <li>WATER_QUALITY_IMPACT – fraction by which water pollution degrades waterQuality
 * </ul>
 */
public final class PollutionModule implements SimulationModule {

    public static final String MODULE_ID = "pollution";

    private static final double WATER_QUALITY_IMPACT = 0.05;
    private static final double CHANGE_THRESHOLD     = 0.01;

    private EcosystemTuning tuning = new EcosystemTuning();

    private static final ModuleMetadata METADATA = new ModuleMetadata(
            MODULE_ID,
            "Pollution",
            "1.0.0",
            "Simulates pollution decay and spread across regions.",
            "1",
            List.of(),
            List.of(),
            true,
            true,
            false);

    @Override
    public String getModuleId() { return MODULE_ID; }

    @Override
    public ModuleMetadata getMetadata() { return METADATA; }

    @Override
    public void initialize(ModuleContext context) {
        if (context == null) throw new IllegalArgumentException("context must not be null");
        if (context.hasService(CoreServices.TUNING)) this.tuning = context.getService(CoreServices.TUNING);
    }

    @Override
    public void onServerStarted(ServerContext context) {}

    @Override
    public void createDefaultRegionData(Region region) {
        if (region == null) throw new IllegalArgumentException("region must not be null");
        if (!region.getModuleData().contains(MODULE_ID)) {
            region.getModuleData().put(MODULE_ID, PollutionRegionData.defaults());
        }
    }

    @Override
    public ModuleUpdateResult updateRegion(RegionUpdateContext context) {
        if (context == null) throw new IllegalArgumentException("context must not be null");

        Region region = context.getRegion();
        PollutionRegionData data = region.getModuleData()
                .get(MODULE_ID, PollutionRegionData.class)
                .orElseGet(PollutionRegionData::defaults);

        double prevPollutionScore = region.getMetrics().getPollutionScore();
        double prevWaterQuality   = region.getMetrics().getWaterQuality();

        // Natural decay: air decays fastest, water slowest (modulated by resistance).
        data.decay(tuning.getPollutionDecayRate());

        // Ground pollution slowly leaches into water even after decay.
        double leach = data.getGroundPollution() * tuning.getGroundToWaterLeach();
        data.addPollution(0.0, 0.0, leach);

        // Summary metric: weighted average emphasising waterPollution as most damaging.
        double pollutionScore = (data.getAirPollution()    * 0.40
                               + data.getGroundPollution() * 0.35
                               + data.getWaterPollution()  * 0.25);
        region.getMetrics().setPollutionScore(pollutionScore);

        // Water quality degrades proportionally to water pollution this tick.
        double waterQualityDrop = data.getWaterPollution() * WATER_QUALITY_IMPACT;
        region.getMetrics().setWaterQuality(
                Math.max(0.0, region.getMetrics().getWaterQuality() - waterQualityDrop));

        // Persist updated data.
        region.getModuleData().put(MODULE_ID, data);

        boolean changed =
                Math.abs(region.getMetrics().getPollutionScore() - prevPollutionScore) > CHANGE_THRESHOLD
             || Math.abs(region.getMetrics().getWaterQuality()   - prevWaterQuality)   > CHANGE_THRESHOLD;

        return changed ? ModuleUpdateResult.changed() : ModuleUpdateResult.noChange();
    }

    @Override
    public void onLivingWorldEvent(LivingWorldEvent event) {}

    @Override
    public void saveModuleData(PersistenceWriter writer) {}

    @Override
    public void loadModuleData(PersistenceReader reader) {}

    @Override
    public void shutdown() {}
}
