package com.livingworld.climate;

import com.livingworld.debug.DiagnosticCategory;
import com.livingworld.debug.LivingWorldLogger;
import com.livingworld.modules.vegetation.VegetationModule;
import com.livingworld.modules.vegetation.VegetationRegionData;
import com.livingworld.regions.Region;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Properties;

/**
 * Tracks planetary-scale atmospheric carbon and its derived climate effects.
 *
 * <h3>Carbon cycle</h3>
 * Each simulation cycle, the tracker aggregates:
 * <ul>
 *   <li>Emissions — proportional to total air pollution across all active regions.
 *   <li>Sink — proportional to total tree canopy pressure across all active regions.
 * </ul>
 * The net delta is applied to the running {@code carbonPpm} value which drifts
 * slowly — meaningful climate change requires sustained industrial activity or
 * sustained reforestation to reverse.
 *
 * <h3>Derived effects</h3>
 * <ul>
 *   <li>{@link #getWarmingLevel()} — 0.0 (pre-industrial) to 1.0 (catastrophic, +5°C).
 *   <li>{@link #getRainPenalty()} — subtracted from every region's atmospheric rain target.
 *   <li>{@link #getBiodiversityIndex()} — average ecosystem health across all active regions.
 * </ul>
 *
 * <h3>Persistence</h3>
 * Carbon state is saved to {@code living_world/global_climate.dat} on server stop and
 * restored on server start, so climate change accumulates across play sessions.
 */
public final class GlobalClimateTracker {

    // Pre-industrial CO₂ baseline (ppm).
    private static final double CARBON_BASELINE  = 280.0;
    // Level at which warming reaches +5°C equivalent (arbitrary but feels right for gameplay).
    private static final double CARBON_CRITICAL  = 800.0;

    // Per sim cycle: each unit of total regional air-pollution score → ppm added.
    private static final double EMISSION_RATE    = 0.05;
    // Per sim cycle: each unit of total tree pressure → ppm removed.
    private static final double SINK_RATE        = 0.002;

    // Maximum rain-target reduction at full warming (applied globally per region).
    private static final double MAX_RAIN_PENALTY = 0.15;

    private double carbonPpm         = CARBON_BASELINE;
    private double warmingLevel      = 0.0; // 0–1
    private double biodiversityIndex = 60.0; // 0–100

    // ------------------------------------------------------------------
    // Simulation update
    // ------------------------------------------------------------------

    /**
     * Updates the global climate state from the current state of all active regions.
     * Must be called once per simulation cycle after modules have run.
     */
    public void update(Collection<Region> regions) {
        if (regions.isEmpty()) return;

        double totalPollution    = 0.0;
        double totalTreePressure = 0.0;
        double totalHealth       = 0.0;
        int    count             = 0;

        for (Region region : regions) {
            totalPollution += region.getMetrics().getPollutionScore();
            VegetationRegionData veg = region.getModuleData()
                    .get(VegetationModule.MODULE_ID, VegetationRegionData.class)
                    .orElse(null);
            if (veg != null) totalTreePressure += veg.getTreePressure();
            totalHealth += region.getMetrics().getEcosystemHealth();
            count++;
        }

        double emissions = totalPollution    * EMISSION_RATE;
        double sink      = totalTreePressure * SINK_RATE;
        carbonPpm = Math.min(CARBON_CRITICAL, Math.max(CARBON_BASELINE, carbonPpm + emissions - sink));
        warmingLevel     = (carbonPpm - CARBON_BASELINE) / (CARBON_CRITICAL - CARBON_BASELINE);
        biodiversityIndex = totalHealth / count;
    }

    // ------------------------------------------------------------------
    // Accessors
    // ------------------------------------------------------------------

    /** Raw atmospheric carbon in ppm (280 = pre-industrial baseline, 800 = critical). */
    public double getCarbonPpm()         { return carbonPpm; }

    /** Warming level 0.0–1.0 (0 = baseline, 1 = +5°C equivalent). */
    public double getWarmingLevel()      { return warmingLevel; }

    /** Temperature anomaly in degrees Celsius equivalent (0–5°C). */
    public double getTemperatureCelsius(){ return warmingLevel * 5.0; }

    /** Average ecosystem health across all active regions (0–100). */
    public double getBiodiversityIndex() { return biodiversityIndex; }

    /**
     * Rain-target penalty applied globally in AtmosphereModule.
     * Increases as warming rises; max {@value MAX_RAIN_PENALTY} at full warming.
     */
    public double getRainPenalty()       { return warmingLevel * MAX_RAIN_PENALTY; }

    // ------------------------------------------------------------------
    // Persistence
    // ------------------------------------------------------------------

    public void save(Path path) {
        try {
            Files.createDirectories(path.getParent());
            Properties props = new Properties();
            props.setProperty("carbonPpm", String.valueOf(carbonPpm));
            try (OutputStream out = Files.newOutputStream(path)) {
                props.store(out, "Living World global climate state — do not edit manually");
            }
        } catch (IOException e) {
            LivingWorldLogger.warn(DiagnosticCategory.BOOTSTRAP,
                    "Failed to save global climate state: " + e.getMessage());
        }
    }

    public void load(Path path) {
        if (!Files.exists(path)) return;
        try {
            Properties props = new Properties();
            try (InputStream in = Files.newInputStream(path)) {
                props.load(in);
            }
            carbonPpm    = Double.parseDouble(props.getProperty("carbonPpm",
                    String.valueOf(CARBON_BASELINE)));
            warmingLevel = (carbonPpm - CARBON_BASELINE) / (CARBON_CRITICAL - CARBON_BASELINE);
            LivingWorldLogger.info(DiagnosticCategory.BOOTSTRAP,
                    String.format("Loaded global climate: %.1f ppm, warming +%.2f°C",
                            carbonPpm, getTemperatureCelsius()));
        } catch (IOException | NumberFormatException e) {
            LivingWorldLogger.warn(DiagnosticCategory.BOOTSTRAP,
                    "Failed to load global climate state — resetting to baseline: " + e.getMessage());
            carbonPpm    = CARBON_BASELINE;
            warmingLevel = 0.0;
        }
    }
}
