"""algebra-symbolic-simplified@v1 π* tests (ticket #000030 Phase 1b).

The simplified variant runs ``sp.simplify`` instead of
``sp.expand``, so trig and exp identities collapse where the
expand-only sibling leaves them distinct. Whole file skips when
SymPy is absent.
"""

from __future__ import annotations

import pytest

sympy = pytest.importorskip("sympy")  # entire file skipped if absent

from arborist.pi_star import get  # noqa: E402
from arborist.pi_star.protocol import (  # noqa: E402
    PiStarError,
    assert_round_trip,
    equivalence_class_id,
)


@pytest.fixture
def ps():
    return get("algebra-symbolic-simplified@v1")


@pytest.fixture
def expand_only():
    return get("algebra-symbolic@v1")


# --- registry presence ------------------------------------------------


def test_registry_contains_simplified():
    ps = get("algebra-symbolic-simplified@v1")
    assert ps.name == "algebra-symbolic-simplified"
    assert ps.version == "v1"
    assert ps.domain == "symbolic-algebra"


# --- trig identity collapses (the main reason this variant exists) --


@pytest.mark.parametrize(
    "left,right",
    [
        ("sin(x)**2 + cos(x)**2", "1"),
        ("tan(x)*cos(x)", "sin(x)"),
        ("sin(x)*cos(y) + cos(x)*sin(y)", "sin(x + y)"),
        ("exp(log(x))", "x"),
    ],
)
def test_trig_exp_identity_collapses_under_simplify(ps, left, right):
    assert ps.canonicalize(left.encode()) == ps.canonicalize(right.encode())
    assert (
        equivalence_class_id(ps, left.encode())
        == equivalence_class_id(ps, right.encode())
    )


def test_trig_collapse_distinguishes_from_expand_sibling(ps, expand_only):
    """The whole point of this variant: sin²+cos² collapses here but
    not under algebra-symbolic@v1."""
    a_simplified = ps.canonicalize(b"sin(x)**2 + cos(x)**2")
    a_expanded = expand_only.canonicalize(b"sin(x)**2 + cos(x)**2")
    one_simplified = ps.canonicalize(b"1")
    one_expanded = expand_only.canonicalize(b"1")
    # Under the simplified variant, both reduce to Integer(1).
    assert a_simplified == one_simplified
    # Under the expand-only sibling, they remain distinct.
    assert a_expanded != one_expanded


# --- polynomial identity still collapses (didn't lose Phase-1 surface) --


@pytest.mark.parametrize(
    "left,right",
    [
        ("(x+1)**2", "x**2 + 2*x + 1"),
        ("(a+b)*(a-b)", "a**2 - b**2"),
        ("2*x + 3*x", "5*x"),
    ],
)
def test_polynomial_identity_still_collapses(ps, left, right):
    assert ps.canonicalize(left.encode()) == ps.canonicalize(right.encode())


# --- distinct expressions stay distinct -------------------------------


@pytest.mark.parametrize(
    "left,right",
    [
        ("x**2", "x**3"),
        ("sin(x)", "cos(x)"),
        # Without positivity assumption, log(exp(x)) does NOT simplify
        # to x — preserves the no-domain-assumptions discipline.
        ("log(exp(x))", "x"),
    ],
)
def test_distinct_inputs_distinct(ps, left, right):
    assert ps.canonicalize(left.encode()) != ps.canonicalize(right.encode())


# --- round-trip / idempotence ----------------------------------------


@pytest.mark.parametrize(
    "expr",
    [
        "x",
        "x + 1",
        "(x+1)**2",
        "sin(x)**2 + cos(x)**2",
        "tan(x)*cos(x)",
        "exp(log(x))",
    ],
)
def test_round_trip_idempotent(ps, expr):
    assert_round_trip(ps, expr.encode())


# --- error paths ------------------------------------------------------


def test_empty_input_raises(ps):
    with pytest.raises(PiStarError, match="empty"):
        ps.canonicalize(b"")


def test_unparseable_input_raises(ps):
    with pytest.raises(PiStarError, match="cannot parse"):
        ps.canonicalize(b"$$$ %% nope")


def test_non_bytes_input_raises(ps):
    with pytest.raises(PiStarError, match="expects bytes"):
        ps.canonicalize("not bytes")  # type: ignore[arg-type]


def test_relational_input_rejected(ps):
    with pytest.raises(PiStarError, match="boolean/relational"):
        ps.canonicalize(b"x > 0")
