package com.livingworld.modules.recovery;

/**
 * Per-region ecological recovery state tracked by {@link RecoveryModule}.
 *
 * <p>Tracks where a region sits in the ecological succession sequence and how much
 * progress it has made toward the next stage.
 *
 * <ul>
 *   <li><b>successionStage</b> – current ecological stage (see {@link SuccessionStage})
 *   <li><b>recoveryProgress</b> – progress (0–100) toward advancing to the next stage
 *   <li><b>damageAccumulation</b> – accumulated ecological damage (0–100); when this
 *       exceeds a threshold the region regresses to the previous stage
 * </ul>
 */
public final class RecoveryRegionData {

    private static final double MIN = 0.0;
    private static final double MAX = 100.0;

    private SuccessionStage successionStage;
    private double recoveryProgress;
    private double damageAccumulation;
    /** Biome-derived ceiling — region cannot advance past this stage. */
    private SuccessionStage maxSuccessionStage;

    public RecoveryRegionData(
            SuccessionStage successionStage,
            double recoveryProgress,
            double damageAccumulation) {
        this(successionStage, recoveryProgress, damageAccumulation, SuccessionStage.MATURE_FOREST);
    }

    public RecoveryRegionData(
            SuccessionStage successionStage,
            double recoveryProgress,
            double damageAccumulation,
            SuccessionStage maxSuccessionStage) {
        if (successionStage == null) {
            throw new IllegalArgumentException("successionStage must not be null");
        }
        if (maxSuccessionStage == null) {
            throw new IllegalArgumentException("maxSuccessionStage must not be null");
        }
        this.successionStage    = successionStage;
        this.recoveryProgress   = clamp(recoveryProgress);
        this.damageAccumulation = clamp(damageAccumulation);
        this.maxSuccessionStage = maxSuccessionStage;
    }

    /** Returns a region at grassland stage with no accumulated damage. */
    public static RecoveryRegionData defaults() {
        return new RecoveryRegionData(SuccessionStage.GRASSLAND, 0.0, 0.0);
    }

    // ------------------------------------------------------------------
    // Getters
    // ------------------------------------------------------------------

    public SuccessionStage getSuccessionStage()    { return successionStage; }
    public double getRecoveryProgress()            { return recoveryProgress; }
    public double getDamageAccumulation()          { return damageAccumulation; }
    public SuccessionStage getMaxSuccessionStage() { return maxSuccessionStage; }

    public void setMaxSuccessionStage(SuccessionStage cap) {
        if (cap == null) throw new IllegalArgumentException("cap must not be null");
        this.maxSuccessionStage = cap;
        // If current stage already exceeds the new cap, clamp it.
        if (successionStage.ordinal() > cap.ordinal()) {
            successionStage = cap;
            recoveryProgress = 0.0;
        }
    }

    // ------------------------------------------------------------------
    // Mutation
    // ------------------------------------------------------------------

    /**
     * Advances recovery progress. If progress reaches 100 and conditions allow,
     * the stage advances and progress resets.
     *
     * @param amount positive progress to add
     * @param soilQuality current soilQuality metric
     * @param pollutionScore current pollutionScore metric
     * @param vegetationPressure current vegetationPressure metric
     */
    public void advanceProgress(double amount,
                                double soilQuality,
                                double pollutionScore,
                                double vegetationPressure) {
        if (amount < 0) throw new IllegalArgumentException("amount must be >= 0");
        recoveryProgress = clamp(recoveryProgress + amount);
        if (recoveryProgress >= 100.0
                && successionStage.hasNext()
                && successionStage.next().ordinal() <= maxSuccessionStage.ordinal()
                && successionStage.conditionsMetForAdvancement(soilQuality, pollutionScore, vegetationPressure)) {
            successionStage   = successionStage.next();
            recoveryProgress  = 0.0;
            damageAccumulation = clamp(damageAccumulation - 10.0); // partial healing on advance
        }
    }

    /**
     * Accumulates ecological damage. If damage exceeds 70, the region regresses
     * one succession stage and damage resets partially.
     *
     * @param amount positive damage to accumulate
     * @param soilQuality current soilQuality metric
     * @param pollutionScore current pollutionScore metric
     * @param vegetationPressure current vegetationPressure metric
     */
    public void accumulateDamage(double amount,
                                 double soilQuality,
                                 double pollutionScore,
                                 double vegetationPressure) {
        if (amount < 0) throw new IllegalArgumentException("amount must be >= 0");
        damageAccumulation = clamp(damageAccumulation + amount);
        if (damageAccumulation >= 70.0
                && successionStage.conditionsMissedForRegression(soilQuality, pollutionScore, vegetationPressure)) {
            successionStage    = successionStage.prev();
            damageAccumulation = 30.0; // partial reset so regression isn't instantaneous
            recoveryProgress   = 0.0;
        }
    }

    /** Clamps all numeric fields to [0, 100]. */
    public void normalize() {
        recoveryProgress  = clamp(recoveryProgress);
        damageAccumulation = clamp(damageAccumulation);
    }

    /** Returns an independent copy. */
    public RecoveryRegionData copy() {
        return new RecoveryRegionData(successionStage, recoveryProgress, damageAccumulation, maxSuccessionStage);
    }

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

    private static double clamp(double v) {
        return Math.min(MAX, Math.max(MIN, v));
    }

    @Override
    public String toString() {
        return "RecoveryRegionData{"
                + "stage=" + successionStage
                + ", progress=" + recoveryProgress
                + ", damage=" + damageAccumulation
                + ", maxStage=" + maxSuccessionStage + "}";
    }
}
