# -*- coding: utf-8 -*-
"""Distribution analyzer that computes statistical properties of grader scores.

This module provides functionality to analyze the statistical distribution of scores
generated by a grader, including measures like mean, median, standard deviation,
minimum and maximum values.
"""

import statistics
from typing import List

from loguru import logger
from pydantic import Field

from openjudge.analyzer.base_analyzer import AnalysisResult, BaseAnalyzer
from openjudge.graders.schema import GraderResult


class DistributionAnalysisResult(AnalysisResult):
    """Result of distribution analysis for a grader.

    This class contains statistical metrics about the distribution of scores
    from a grader.

    Attributes:
        mean (float): The mean score.
        median (float): The median score.
        stdev (float): The standard deviation of scores.
        min_score (float): The minimum score.
        max_score (float): The maximum score.

    Example:
        >>> result = DistributionAnalysisResult(
        ...     name="test_grader",
        ...     mean=0.75,
        ...     median=0.8,
        ...     stdev=0.1,
        ...     min_score=0.5,
        ...     max_score=0.9
        ... )
        >>> print(result.name)
        test_grader
        >>> print(f"Mean score: {result.mean}")
        Mean score: 0.75
    """

    mean: float = Field(
        default=0.0,
        description="The mean score",
    )
    median: float = Field(
        default=0.0,
        description="The median score",
    )
    stdev: float = Field(
        default=0.0,
        description="The standard deviation of scores",
    )
    min_score: float = Field(
        default=0.0,
        description="The minimum score",
    )
    max_score: float = Field(
        default=0.0,
        description="The maximum score",
    )


class DistributionAnalyzer(BaseAnalyzer):
    """Analyzer for computing statistical distribution of grader scores.

    This analyzer computes statistical properties of the scores produced by
    a grader, such as mean, median, standard deviation, min, and max values.
    It works solely on the grader results without requiring ground truth data.

    Attributes:
        name (str): Name of the analyzer, defaults to "Distribution Analysis".

    Example:
        >>> analyzer = DistributionAnalyzer()
        >>> print(analyzer.name)
        Distribution Analysis
    """

    name: str = "Distribution Analysis"

    def analyze(
        self,
        dataset: List[dict],
        grader_results: List[GraderResult],
        **kwargs,
    ) -> DistributionAnalysisResult:
        """Compute the statistical distribution of grader scores.

        Calculates statistical properties of the scores including mean, median,
        standard deviation, minimum and maximum values.

        Args:
            dataset: The data samples that were evaluated.
            grader_results: The evaluation results from a single
                grader, organized as a list of GraderResult objects, one for each sample.
            **kwargs: Additional keyword arguments.

        Returns:
            DistributionAnalysisResult: The computed distribution analysis result containing
            statistical metrics with metadata.

        Example:
            >>> from openjudge.graders.schema import GraderResult
            >>> dataset = [
            ...     {"input": "query1"},
            ...     {"input": "query2"}
            ... ]
            >>> grader_results = [
            ...     GraderResult(name="grader1", score=0.8, reason="Good"),
            ...     GraderResult(name="grader1", score=0.6, reason="Acceptable")
            ... ]
            >>> analyzer = DistributionAnalyzer()
            >>> result = analyzer.analyze(dataset, grader_results)
            >>> print(f"Mean score: {result.mean}")
            Mean score: 0.7
            >>> print(f"Standard deviation: {result.stdev}")
            Standard deviation: 0.1
        """
        if not grader_results:
            logger.warning(
                "No grader results provided for distribution calculation",
            )
            return DistributionAnalysisResult(
                name=self.name,
                mean=0.0,
                median=0.0,
                stdev=0.0,
                min_score=0.0,
                max_score=0.0,
                metadata={
                    "explanation": "No grader results provided for distribution calculation",
                },
            )

        # Extract scores from grader results
        scores = []
        for grader_result in grader_results:
            if grader_result and hasattr(grader_result, "score") and grader_result.score is not None:
                scores.append(float(grader_result.score))

        if not scores:
            return DistributionAnalysisResult(
                name=self.name,
                mean=0.0,
                median=0.0,
                stdev=0.0,
                min_score=0.0,
                max_score=0.0,
                metadata={
                    "explanation": "No valid scores found in grader results",
                },
            )

        # Calculate statistical metrics
        mean_score = statistics.mean(scores)
        median_score = statistics.median(scores)
        min_score = min(scores)
        max_score = max(scores)

        # Calculate standard deviation if we have more than one score
        stdev_score = 0.0
        if len(scores) > 1:
            stdev_score = statistics.stdev(scores)

        explanation = (
            f"Analyzed {len(scores)} scores with mean={mean_score:.3f}, "
            f"median={median_score:.3f}, stdev={stdev_score:.3f}"
        )

        return DistributionAnalysisResult(
            name=self.name,
            mean=mean_score,
            median=median_score,
            stdev=stdev_score,
            min_score=min_score,
            max_score=max_score,
            metadata={
                "explanation": explanation,
                "total_samples": len(scores),
            },
        )
