package sd.analysis;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Utilitário estático para processamento matemático e análise estatística dos dados da simulação.
 * <p>
 * Esta classe fornece algoritmos para cálculo de medidas de tendência central (média, mediana),
 * dispersão (desvio padrão amostral) e inferência estatística (Intervalos de Confiança).
 * É utilizada para normalizar e validar os resultados estocásticos obtidos através de
 * múltiplas execuções do sistema.
 */
public class StatisticalAnalysis {
    
    /**
     * Calcula a média aritmética de um conjunto de valores.
     * * @param values Lista de valores numéricos (double).
     * @return A soma dos valores dividida pelo tamanho da amostra, ou 0.0 se a lista for nula/vazia.
     */
    public static double mean(List<Double> values) {
        if (values == null || values.isEmpty()) {
            return 0.0;
        }
        double sum = 0.0;
        for (double value : values) {
            sum += value;
        }
        return sum / values.size();
    }
    
    /**
     * Calcula o desvio padrão amostral (sample standard deviation).
     * <p>
     * Utiliza o denominador {@code n - 1} (Correção de Bessel) para fornecer um
     * estimador não viesado da variância populacional, adequado para as amostras
     * de simulação.
     * * @param values Lista de observações.
     * @return O desvio padrão calculado, ou 0.0 se o tamanho da amostra for < 2.
     */
    public static double standardDeviation(List<Double> values) {
        if (values == null || values.size() < 2) {
            return 0.0;
        }
        
        double mean = mean(values);
        double sumSquaredDiff = 0.0;
        
        for (double value : values) {
            double diff = value - mean;
            sumSquaredDiff += diff * diff;
        }
        
        // Sample standard deviation (n-1 denominator)
        return Math.sqrt(sumSquaredDiff / (values.size() - 1));
    }
    
    /**
     * Calcula o Intervalo de Confiança (IC) de 95% para a média.
     * <p>
     * Utiliza a distribuição t de Student para maior precisão em amostras pequenas (n < 30),
     * onde a aproximação pela distribuição Normal (Z) seria inadequada. O intervalo define
     * a faixa onde a verdadeira média populacional reside com 95% de probabilidade.
     * * @param values Lista de observações.
     * @return Um array de double onde índice 0 é o limite inferior e índice 1 é o limite superior.
     */
    public static double[] confidenceInterval95(List<Double> values) {
        if (values == null || values.size() < 2) {
            double m = mean(values);
            return new double[]{m, m};
        }
        
        double mean = mean(values);
        double stdDev = standardDeviation(values);
        int n = values.size();
        
        // Critical value from t-distribution (approximation for common sample sizes)
        double tCritical = getTCriticalValue(n);
        
        // Standard error of the mean
        double standardError = stdDev / Math.sqrt(n);
        
        // Margin of error
        double marginOfError = tCritical * standardError;
        
        return new double[]{
            mean - marginOfError,  // Lower bound
            mean + marginOfError   // Upper bound
        };
    }
    
    /**
     * Retorna o valor crítico t (t-score) para um IC de 95% (bicaudal).
     * <p>
     * Baseia-se nos graus de liberdade (gl = n - 1). Para amostras grandes (gl >= 30),
     * aproxima-se do valor Z de 1.96.
     * * @param sampleSize O tamanho da amostra (n).
     * @return O fator multiplicativo t apropriado.
     */
    private static double getTCriticalValue(int sampleSize) {
        int df = sampleSize - 1;  // degrees of freedom
        
        // t-critical values for 95% confidence (two-tailed)
        if (df >= 30) return 1.96;      // z-score for large samples
        if (df >= 20) return 2.086;
        if (df >= 15) return 2.131;
        if (df >= 10) return 2.228;
        if (df >= 5) return 2.571;
        if (df >= 3) return 3.182;
        if (df >= 2) return 4.303;
        return 12.706;  // df = 1
    }
    
    /**
     * Identifica o valor mínimo absoluto na amostra.
     * * @param values Lista de valores.
     * @return O menor valor encontrado.
     */
    public static double min(List<Double> values) {
        if (values == null || values.isEmpty()) {
            return 0.0;
        }
        return Collections.min(values);
    }
    
    /**
     * Identifica o valor máximo absoluto na amostra.
     * * @param values Lista de valores.
     * @return O maior valor encontrado.
     */
    public static double max(List<Double> values) {
        if (values == null || values.isEmpty()) {
            return 0.0;
        }
        return Collections.max(values);
    }
    
    /**
     * Calcula a mediana da amostra.
     * <p>
     * <b>Nota de Desempenho:</b> Este método ordena uma cópia da lista, resultando em
     * complexidade O(n log n).
     * * @param values Lista de valores.
     * @return O valor central (ou média dos dois centrais) da distribuição ordenada.
     */
    public static double median(List<Double> values) {
        if (values == null || values.isEmpty()) {
            return 0.0;
        }
        
        List<Double> sorted = new ArrayList<>(values);
        Collections.sort(sorted);
        
        int size = sorted.size();
        if (size % 2 == 0) {
            return (sorted.get(size / 2 - 1) + sorted.get(size / 2)) / 2.0;
        } else {
            return sorted.get(size / 2);
        }
    }
    
    /**
     * Formata um sumário estatístico completo para uma métrica específica.
     * <p>
     * Útil para logging e geração de relatórios textuais.
     * * @param metricName Nome da métrica a ser exibida.
     * @param values Os dados brutos associados à métrica.
     * @return String formatada contendo Média, Desvio Padrão, IC95%, Min, Max e N.
     */
    public static String formatSummary(String metricName, List<Double> values) {
        if (values == null || values.isEmpty()) {
            return metricName + ": No data";
        }
        
        double mean = mean(values);
        double stdDev = standardDeviation(values);
        double[] ci = confidenceInterval95(values);
        double min = min(values);
        double max = max(values);
        
        return String.format(
            "%s:\n" +
            "  Mean: %.2f\n" +
            "  Std Dev: %.2f\n" +
            "  95%% CI: [%.2f, %.2f]\n" +
            "  Min: %.2f\n" +
            "  Max: %.2f\n" +
            "  Samples: %d",
            metricName, mean, stdDev, ci[0], ci[1], min, max, values.size()
        );
    }
}