import decimal as dec

import pytest
from miska.types.numeric import Percent


def test_construct_and_str_repr():
    p1 = Percent(25)
    p2 = Percent("12.5")
    p3 = Percent(dec.Decimal("7.5"))
    p4 = Percent("10%")  # trailing percent sign is accepted

    assert str(p1) == "25%"
    assert repr(p2) == "Percent(12.5)"
    assert str(p3) == "7.5%"
    assert str(p4) == "10%"


def test_ratio_and_as_fraction_and_from_ratio():
    p = Percent("12.5")
    assert p.ratio == dec.Decimal("0.125")
    assert p.as_fraction() == dec.Decimal("0.125")

    from_r = Percent.from_ratio("0.075")
    assert isinstance(from_r, Percent)
    assert str(from_r) == "7.5%"


def test_apply_to_values_returns_decimal():
    p = Percent(25)
    assert p.apply(200) == dec.Decimal(50)
    assert p.apply("200") == dec.Decimal(50)


def test_add_subtract_with_percent_only():
    p = Percent("12.5")
    q = Percent("2.5")
    assert p + q == Percent("15")
    assert isinstance(p + q, Percent)
    assert Percent(50) - Percent(12) == Percent(38)

    with pytest.raises(TypeError):
        _ = p + 5  # type: ignore[operator]
    with pytest.raises(TypeError):
        _ = p - 1  # type: ignore[operator]


def test_multiply_divide_with_numeric_scalars():
    p = Percent(10)
    # int
    assert p * 3 == Percent(30)
    # float (0.5 is exactly representable)
    assert p * 0.5 == Percent("5")
    # Decimal
    assert p * dec.Decimal("2.5") == Percent("25")

    assert Percent(10) / 4 == Percent("2.5")
    assert Percent(10) / dec.Decimal("2.5") == Percent("4")

    with pytest.raises(TypeError):
        _ = p * Percent(2)  # type: ignore[operator]
    with pytest.raises(TypeError):
        _ = p / Percent(2)  # type: ignore[operator]
    with pytest.raises(TypeError):
        _ = p * "2"  # type: ignore[operator]
    with pytest.raises(TypeError):
        _ = p / "2"  # type: ignore[operator]


def test_urn_roundtrip_and_errors():
    # to URN
    u = Percent("12.5").as_urn()
    assert hasattr(u, "to_string")
    assert u.to_string() == "urn:percent:12.5"

    # from URN string
    assert Percent.from_urn("urn:percent:7.5") == Percent("7.5")

    # from parsed URN object
    import urnparse

    parsed = urnparse.URN8141.from_string("urn:percent:42")
    assert Percent.from_urn(parsed) == Percent(42)

    # invalid URNs
    with pytest.raises(ValueError):
        Percent.from_urn("not a urn")
    with pytest.raises(ValueError):
        Percent.from_urn("urn:percent")  # wrong segments
    with pytest.raises(ValueError):
        Percent.from_urn("urn:other:12")  # wrong namespace id
