"""
Unit tests for BoxplotChartStrategy._prepare_plot_data().
"""

from typing import Any
from unittest.mock import MagicMock

import polars as pl
import pytest
from fields_metadata import FieldMetadata
from polychromos import HSLColor

from orama.color import (
    ColorAssignerByCategory,
    ColorRule,
    ColorSelectorStrategy,
)
from orama.strategies.boxplot import BoxplotChartStrategy


def _cat_meta() -> MagicMock:
    m = MagicMock(spec=FieldMetadata)
    m.categorical = True
    m.numeric = False
    m.derived = False
    m.effective_type = str
    m.extra = {}
    return m


def _num_meta() -> MagicMock:
    m = MagicMock(spec=FieldMetadata)
    m.categorical = False
    m.numeric = True
    m.derived = False
    m.effective_type = float
    m.extra = {}
    return m


def _make_boxplot(**kwargs: Any) -> BoxplotChartStrategy:
    return BoxplotChartStrategy(
        fields_metadata={"cat": _cat_meta(), "val": _num_meta()},
        category_variables=["cat"],
        value_variable="val",
        **kwargs,
    )


def _stats_df(categories: list[str]) -> pl.DataFrame:
    """Build a minimal statistics DataFrame as would come from a query."""
    n = len(categories)
    return pl.DataFrame(
        {
            "cat": categories,
            "q1": [1.0] * n,
            "median": [2.0] * n,
            "q3": [3.0] * n,
            "mean": [2.0] * n,
            "sd": [0.5] * n,
            "min": [0.0] * n,
            "max": [4.0] * n,
        }
    )


@pytest.fixture
def stats_df() -> pl.DataFrame:
    return _stats_df(["A", "B"])


def test_prepare_plot_data_returns_categories_statistics_colors(
    stats_df: pl.DataFrame,
    tr: Any,
    theme: Any,
) -> None:
    boxplot = _make_boxplot()
    result = boxplot._prepare_plot_data({"main": stats_df}, tr, theme)
    assert result["categories"] == ["A", "B"]
    assert len(result["statistics"]) == 2
    assert len(result["colors"]) == 2
    assert all(c.startswith("#") for c in result["colors"])
    # _compute_fences must have added fences to each statistics dict
    for stat in result["statistics"]:
        assert "lowerfence" in stat
        assert "upperfence" in stat


def test_prepare_plot_data_overall_category_appended(
    stats_df: pl.DataFrame,
    tr: Any,
    theme: Any,
) -> None:
    overall_df = _stats_df([""])
    # overall query has no category columns — just the stats
    overall_df = overall_df.drop("cat")
    boxplot = _make_boxplot(overall_category_name="Total")
    result = boxplot._prepare_plot_data({"main": stats_df, "overall": overall_df}, tr, theme)
    assert result["categories"][-1] == "Total"
    assert len(result["categories"]) == 3


def test_prepare_plot_data_color_rules_applied(
    stats_df: pl.DataFrame,
    anchor_colors: list[HSLColor],
    tr: Any,
    theme: Any,
) -> None:
    rule = ColorRule(
        assigner=ColorAssignerByCategory(anchor_colors=anchor_colors),
        selector=ColorSelectorStrategy(),
    )
    boxplot = _make_boxplot(color_rules=[rule])
    result = boxplot._prepare_plot_data({"main": stats_df}, tr, theme)
    assert len(result["colors"]) == 2
    assert all(c.startswith("#") for c in result["colors"])
