package fr.luc.gamingcore.economy;

import fr.luc.gamingcore.model.Guild;

/**
 * Calculateur du coût des chunks territoriaux utilisant la formule spatio-temporelle avec surchauffe glissante.
 */
public class ChunkPriceCalculator {

    // Paramètres spatiaux
    private double basePrice = 1000.0;
    private double growthRate = 0.08;
    private double alpha = 0.95;
    private double linearStep = 150.0;

    // Paramètres temporels de surchauffe (fenêtre 24h)
    private double kBase = 0.05;
    private double kSize = 0.03;
    private double delta = 1.15;
    private long recentWindowMillis = 24L * 60L * 60L * 1000L; // 24 heures

    /**
     * Constructeur par défaut avec les paramètres recommandés.
     */
    public ChunkPriceCalculator() {}

    /**
     * Constructeur personnalisé permettant de configurer l'ensemble des paramètres de la formule.
     *
     * @param basePrice          Prix de base du premier chunk.
     * @param growthRate         Taux de croissance exponentiel de base (ex: 0.08 pour +8%).
     * @param alpha              Facteur d'amortissement de l'exposant.
     * @param linearStep         Ajout linéaire fixe par chunk possédé.
     * @param kBase              Surchauffe minimale par claim récent (ex: 0.05 pour +5%).
     * @param kSize              Coefficient d'amplification de la surchauffe par tranche de 10 chunks possédés.
     * @param delta              Exposant d'agressivité de la surchauffe temporelle.
     * @param recentWindowMillis Fenêtre de temps glissante en millisecondes (ex: 24h).
     */
    public ChunkPriceCalculator(double basePrice, double growthRate, double alpha, double linearStep,
                                double kBase, double kSize, double delta, long recentWindowMillis) {
        this.basePrice = Math.max(1.0, basePrice);
        this.growthRate = Math.max(0.0, growthRate);
        this.alpha = Math.max(0.1, alpha);
        this.linearStep = Math.max(0.0, linearStep);
        this.kBase = Math.max(0.0, kBase);
        this.kSize = Math.max(0.0, kSize);
        this.delta = Math.max(0.5, delta);
        this.recentWindowMillis = Math.max(1000L, recentWindowMillis);
    }

    /**
     * Calcule le prix d'achat du prochain chunk pour une guilde donnée.
     * Prend en compte son nombre total de chunks et ses achats récents dans les dernières 24h.
     *
     * @param guild La guilde souhaitant effectuer le claim.
     * @return Le prix exact calculé et arrondi à 2 décimales.
     */
    public double calculateNextChunkPrice(Guild guild) {
        if (guild == null) {
            return basePrice;
        }

        int currentChunks = guild.getClaimCount();
        long cutoff = System.currentTimeMillis() - recentWindowMillis;
        int recentClaims = guild.getRecentClaimsCount(cutoff);

        return calculatePrice(currentChunks + 1, recentClaims);
    }

    /**
     * Calcule le prix d'un chunk selon le numéro du chunk visé et le nombre de claims récents.
     *
     * @param chunkNumber       Numéro du chunk à acquérir (N >= 1).
     * @param recentClaimsCount Nombre de claims effectués dans les dernières 24h (R >= 0).
     * @return Le coût final après application de la surchauffe.
     */
    public double calculatePrice(int chunkNumber, int recentClaimsCount) {
        int n = Math.max(1, chunkNumber);
        int r = Math.max(0, recentClaimsCount);

        double structuralPrice = calculateStructuralPrice(n);
        double overheatMultiplier = calculateOverheatMultiplier(n, r);

        double rawFinal = structuralPrice * overheatMultiplier;
        return Math.round(rawFinal * 100.0) / 100.0;
    }

    /**
     * Calcule le prix structurel de base lié uniquement à la taille de la guilde :
     * P_structurel(N) = P_base * (1 + Taux)^((N-1)^alpha) + (N-1) * Palier
     *
     * @param chunkNumber Numéro du chunk (N >= 1).
     * @return Le prix structurel hors surchauffe.
     */
    public double calculateStructuralPrice(int chunkNumber) {
        int n = Math.max(1, chunkNumber);
        if (n == 1) {
            return basePrice;
        }

        double exponent = Math.pow(n - 1, alpha);
        double geometricPart = basePrice * Math.pow(1.0 + growthRate, exponent);
        double linearPart = (n - 1) * linearStep;

        return geometricPart + linearPart;
    }

    /**
     * Calcule le multiplicateur de surchauffe temporelle :
     * M_surchauffe(N, R) = 1 + (k_base + k_taille * (N / 10)) * R^delta
     *
     * @param chunkNumber       Nombre de chunks de la guilde.
     * @param recentClaimsCount Nombre de claims récents (dernières 24h).
     * @return Le coefficient multiplicateur (>= 1.0).
     */
    public double calculateOverheatMultiplier(int chunkNumber, int recentClaimsCount) {
        if (recentClaimsCount <= 0) {
            return 1.0;
        }

        int n = Math.max(1, chunkNumber);
        int r = recentClaimsCount;

        double sizeScaledFactor = kBase + (kSize * (n / 10.0));
        double velocityFactor = Math.pow(r, delta);

        return 1.0 + (sizeScaledFactor * velocityFactor);
    }

    /**
     * Calcule le coût cumulé total pour acheter une série de plusieurs chunks consécutifs.
     *
     * @param guild       La guilde concernée.
     * @param chunksToBuy Le nombre de chunks à acheter d'un coup.
     * @return La somme totale des prix de chaque chunk avec surchauffe progressive.
     */
    public double calculateTotalPriceForMultipleChunks(Guild guild, int chunksToBuy) {
        if (guild == null || chunksToBuy <= 0) {
            return 0.0;
        }

        int currentChunks = guild.getClaimCount();
        long cutoff = System.currentTimeMillis() - recentWindowMillis;
        int recentClaims = guild.getRecentClaimsCount(cutoff);

        double total = 0.0;
        for (int i = 0; i < chunksToBuy; i++) {
            int targetN = currentChunks + 1 + i;
            int targetR = recentClaims + i;
            total += calculatePrice(targetN, targetR);
        }

        return Math.round(total * 100.0) / 100.0;
    }

    // Getters et Setters pour la configuration dynamique
    public double getBasePrice() { return basePrice; }
    public void setBasePrice(double basePrice) { this.basePrice = basePrice; }

    public double getGrowthRate() { return growthRate; }
    public void setGrowthRate(double growthRate) { this.growthRate = growthRate; }

    public double getAlpha() { return alpha; }
    public void setAlpha(double alpha) { this.alpha = alpha; }

    public double getLinearStep() { return linearStep; }
    public void setLinearStep(double linearStep) { this.linearStep = linearStep; }

    public double getKBase() { return kBase; }
    public void setKBase(double kBase) { this.kBase = kBase; }

    public double getKSize() { return kSize; }
    public void setKSize(double kSize) { this.kSize = kSize; }

    public double getDelta() { return delta; }
    public void setDelta(double delta) { this.delta = delta; }

    public long getRecentWindowMillis() { return recentWindowMillis; }
    public void setRecentWindowMillis(long recentWindowMillis) { this.recentWindowMillis = recentWindowMillis; }
}
