import numpy
import pint
import pytest

from ..units import convert_units_to_group_reference
from ..units import convert_values_to_units


def test_group_reference_already_normalized():
    values = [numpy.array([1.0]), numpy.array([2.0])]
    units = ["mm", "mm"]

    new_values, new_units = convert_units_to_group_reference(
        values, units, fallback="largest"
    )

    assert new_units == ["mm", "mm"]
    assert numpy.allclose(new_values, [[1.0], [2.0]])


def test_group_reference_first_fallback():
    values = [numpy.array([1.0]), numpy.array([1.0])]
    units = ["mm", "um"]

    new_values, new_units = convert_units_to_group_reference(
        values, units, fallback="first"
    )

    assert new_units == ["mm", "mm"]
    assert numpy.allclose(new_values[1], [0.001])


def test_group_reference_largest_fallback():
    values = [numpy.array([1.0]), numpy.array([1000.0])]
    units = ["mm", "um"]

    new_values, new_units = convert_units_to_group_reference(
        values, units, fallback="largest"
    )

    assert new_units == ["mm", "mm"]
    assert numpy.allclose(new_values[1], [1.0])


def test_group_reference_smallest_fallback():
    values = [numpy.array([1.0]), numpy.array([1.0])]
    units = ["mm", "um"]

    new_values, new_units = convert_units_to_group_reference(
        values, units, fallback="smallest"
    )

    assert new_units == ["um", "um"]
    assert numpy.allclose(new_values[0], [1000.0])


def test_group_reference_explicit_override():
    values = [numpy.array([1.0]), numpy.array([1.0])]
    units = ["mm", "um"]

    new_values, new_units = convert_units_to_group_reference(
        values,
        units,
        reference_units={"[length]": "mm"},
    )

    assert new_units == ["mm", "mm"]
    assert numpy.allclose(new_values[1], [0.001])


def test_group_reference_mixed_dimensions():
    values = [
        numpy.array([1.0]),
        numpy.array([1000.0]),
        numpy.array([180]),
        numpy.array([numpy.pi]),
    ]
    units = ["m", "mm", "deg", "rad"]

    new_values, new_units = convert_units_to_group_reference(
        values,
        units,
        reference_units={"dimensionless": "deg"},
        fallback="largest",
    )

    assert new_units == ["m", "m", "deg", "deg"]
    assert numpy.allclose(new_values, [[1], [1], [180], [180]])


def test_group_reference_no_fallback_keeps_units_partial_override():
    values = [
        numpy.array([1.0]),
        numpy.array([1000.0]),
        numpy.array([180]),
        numpy.array([numpy.pi]),
    ]
    units = ["m", "mm", "deg", "rad"]

    new_values, new_units = convert_units_to_group_reference(
        values,
        units,
        reference_units={"dimensionless": "deg"},
        fallback=None,
    )

    assert new_units == ["m", "mm", "deg", "deg"]
    assert numpy.allclose(new_values, [[1], [1000], [180], [180]])


def test_group_reference_dimensionless():
    values = [numpy.array([1.0]), numpy.array([2.0])]
    units = [None, None]

    new_values, new_units = convert_units_to_group_reference(values, units)

    assert new_units == ["dimensionless", "dimensionless"]
    assert numpy.allclose(new_values, [[1.0], [2.0]])


def test_group_reference_invalid_fallback():
    values = [numpy.array([1.0])]
    units = ["m"]

    with pytest.raises(ValueError):
        convert_units_to_group_reference(values, units, fallback="invalid")


def test_convert_values_simple():
    values = [1.0, 2.0]
    target_units = ["m", "s"]

    result = convert_values_to_units(values, target_units)

    assert result == [1.0, 2.0]


def test_convert_values_with_units():
    values = [(1000.0, "mm"), (2.0, "s")]
    target_units = ["m", "s"]

    result = convert_values_to_units(values, target_units)

    assert result == [1.0, 2.0]


def test_convert_values_mixed_inputs():
    values = [(1000.0, "mm"), 2.0]
    target_units = ["m", "s"]

    result = convert_values_to_units(values, target_units)

    assert result == [1.0, 2.0]


def test_convert_values_none_input():
    result = convert_values_to_units(None, ["m", "s"])
    assert result is None


def test_convert_values_dimensionless():
    values = [(1.0, "dimensionless")]
    target_units = [None]

    result = convert_values_to_units(values, target_units)

    assert result == [1.0]


def test_convert_values_incompatible_units():
    values = [(1.0, "m")]
    target_units = ["s"]

    with pytest.raises(pint.DimensionalityError):
        convert_values_to_units(values, target_units)


def test_convert_values_precision():
    values = [(1.0, "km")]
    target_units = ["m"]

    result = convert_values_to_units(values, target_units)

    assert result == [1000.0]


def test_group_reference_partial_override_with_fallback():
    values = [numpy.array([1.0]), numpy.array([1000.0])]
    units = ["mm", "um"]

    new_values, new_units = convert_units_to_group_reference(
        values,
        units,
        reference_units={},
        fallback="largest",
    )

    assert new_units == ["mm", "mm"]
    assert numpy.allclose(new_values[1], [1.0])
