"""
Unit tests for form_factory helper functions and _ENUM_OPTION_TYPES.
"""

from enum import Enum
from typing import Any, cast
from unittest.mock import MagicMock

import pytest
from babel.support import Translations
from fields_metadata import FieldMetadata
from wtforms import SelectField

from orama.enums import (
    GeoProjection,
    LineInterpolation,
    LinePlacement,
    LineSortOrder,
    SortOrder,
)
from orama.options import (
    GeoProjectionOption,
    LineInterpolationOption,
    LinePlacementOption,
    LineSortOrderOption,
    SortOrderOption,
    StringOption,
)
from orama.strategies.bar import BarChartStrategy
from orama.web.form_factory import (
    _ENUM_OPTION_TYPES,
    LBL_PREFIX,
    OPT_PREFIX,
    VAR_PREFIX,
    _add_option_field,
    _add_variable_field,
    create_figure_form_class,
)


def test_enum_option_types_keys() -> None:
    expected = {SortOrderOption, LinePlacementOption, LineInterpolationOption, LineSortOrderOption, GeoProjectionOption}
    assert set(_ENUM_OPTION_TYPES.keys()) == expected


def test_enum_option_types_sort_order_has_coerce() -> None:
    _, extra = _ENUM_OPTION_TYPES[SortOrderOption]
    assert extra == {"coerce": int}


@pytest.mark.parametrize(
    "opt_type,enum_cls",
    [
        (SortOrderOption, SortOrder),
        (LinePlacementOption, LinePlacement),
        (LineInterpolationOption, LineInterpolation),
        (LineSortOrderOption, LineSortOrder),
        (GeoProjectionOption, GeoProjection),
    ],
)
def test_add_option_field_enum_produces_select_field(
    opt_type: type,
    enum_cls: type[Enum],
) -> None:
    option = opt_type(
        name="Test",
        description="Test option",
        map_to="test_field",
        default_value=list(enum_cls)[0],
    )
    opt_desc = option.describe()
    attrs: dict[str, Any] = {}
    _add_option_field(attrs, opt_desc)
    key = f"{OPT_PREFIX}test_field"
    assert key in attrs
    assert attrs[key].field_class is SelectField
    expected_values = [e.value for e in enum_cls]
    field_values = [choice[0] for choice in attrs[key].kwargs["choices"]]
    assert field_values == expected_values


def test_create_figure_form_class_bar_strategy() -> None:
    cat_meta = MagicMock(spec=FieldMetadata)
    cat_meta.categorical = True
    cat_meta.numeric = False
    cat_meta.derived = False
    cat_meta.effective_type = str
    cat_meta.extra = {}
    num_meta = MagicMock(spec=FieldMetadata)
    num_meta.categorical = False
    num_meta.numeric = True
    num_meta.derived = False
    num_meta.effective_type = float
    num_meta.extra = {}
    fields_metadata = cast(dict[str, FieldMetadata], {"category": cat_meta, "value": num_meta})
    form_cls = create_figure_form_class(BarChartStrategy, fields_metadata)
    from wtforms import Form

    assert issubclass(form_cls, Form)
    form_cls()


@pytest.fixture
def wrapping_tr() -> MagicMock:
    mock = MagicMock(spec=Translations)
    mock.gettext.side_effect = lambda s: f"[{s}]"
    return mock


@pytest.fixture
def bar_fields_metadata() -> dict[str, MagicMock]:
    cat_meta = MagicMock(spec=FieldMetadata)
    cat_meta.categorical = True
    cat_meta.numeric = False
    cat_meta.derived = False
    cat_meta.effective_type = str
    cat_meta.extra = {}
    num_meta = MagicMock(spec=FieldMetadata)
    num_meta.categorical = False
    num_meta.numeric = True
    num_meta.derived = False
    num_meta.effective_type = float
    num_meta.extra = {}
    return {"category": cat_meta, "value": num_meta}


def test_create_figure_form_class_translates_fixed_labels(
    wrapping_tr: MagicMock,
    bar_fields_metadata: dict[str, MagicMock],
) -> None:
    fields_metadata = cast(dict[str, FieldMetadata], bar_fields_metadata)
    form_cls = create_figure_form_class(BarChartStrategy, fields_metadata, tr=wrapping_tr)
    form = form_cls()
    assert form[f"{LBL_PREFIX}title"].label.text == "[Title]"
    assert form[f"{LBL_PREFIX}subtitle"].label.text == "[Subtitle]"
    assert form[f"{LBL_PREFIX}caption"].label.text == "[Caption]"
    assert form[f"{LBL_PREFIX}description"].label.text == "[Description]"
    assert form[f"{LBL_PREFIX}storytelling_context"].label.text == "[Storytelling Context]"


def test_create_figure_form_class_no_tr_keeps_original_labels(
    bar_fields_metadata: dict[str, MagicMock],
) -> None:
    fields_metadata = cast(dict[str, FieldMetadata], bar_fields_metadata)
    form_cls = create_figure_form_class(BarChartStrategy, fields_metadata)
    form = form_cls()
    assert form[f"{LBL_PREFIX}title"].label.text == "Title"
    assert form[f"{LBL_PREFIX}subtitle"].label.text == "Subtitle"


def test_add_option_field_translates_enum_labels(wrapping_tr: MagicMock) -> None:
    option = SortOrderOption(
        name="Sort Order",
        description="Sort direction",
        map_to="sort_order",
        default_value=SortOrder.NONE,
    )
    opt_desc = option.describe()
    attrs: dict[str, Any] = {}
    _add_option_field(attrs, opt_desc, tr=wrapping_tr)
    key = f"{OPT_PREFIX}sort_order"
    assert attrs[key].args[0] == "[Sort Order]"
    display_names = [label for _, label in attrs[key].kwargs["choices"]]
    assert all(name.startswith("[") and name.endswith("]") for name in display_names)


def test_add_option_field_translates_string_option_label(wrapping_tr: MagicMock) -> None:
    option = StringOption(
        name="My Label",
        description="A string option",
        map_to="my_label",
    )
    opt_desc = option.describe()
    attrs: dict[str, Any] = {}
    _add_option_field(attrs, opt_desc, tr=wrapping_tr)
    key = f"{OPT_PREFIX}my_label"
    assert attrs[key].args[0] == "[My Label]"


def test_add_variable_field_aggregation_includes_count(
    bar_fields_metadata: dict[str, MagicMock],
) -> None:
    from therismos.grouping import AggregationFunction

    from orama.variables import NumericalAggregationVariable

    fields_metadata = cast(dict[str, FieldMetadata], bar_fields_metadata)
    var_desc = NumericalAggregationVariable(
        name="Value",
        description="A numerical aggregation variable",
        map_to="value_variable",
    ).describe(fields_metadata)
    attrs: dict[str, Any] = {}
    _add_variable_field(attrs, var_desc, fields_metadata)
    function_key = f"{VAR_PREFIX}value_variable__function"
    assert function_key in attrs
    function_values = [value for value, _ in attrs[function_key].kwargs["choices"]]
    assert AggregationFunction.COUNT.value in function_values


def test_add_variable_field_translates_label_and_none_placeholder(
    wrapping_tr: MagicMock,
    bar_fields_metadata: dict[str, MagicMock],
) -> None:
    from orama.variables import CategoricalVariable

    fields_metadata = cast(dict[str, FieldMetadata], bar_fields_metadata)
    var_desc = CategoricalVariable(
        name="Category",
        description="A category variable",
        map_to="category_variable",
        optional=True,
    ).describe(fields_metadata)
    attrs: dict[str, Any] = {}
    _add_variable_field(attrs, var_desc, fields_metadata, tr=wrapping_tr)
    key = f"{VAR_PREFIX}category_variable"
    assert attrs[key].args[0] == "[Category]"
    none_labels = [label for value, label in attrs[key].kwargs["choices"] if value == ""]
    assert none_labels == ["[— none —]"]
