import pytest
import rusty_jq
import json


@pytest.fixture
def data():
    return json.dumps({
        "price": 100,
        "tax": 20,
        "quantity": 3,
        "discount": 0.1,
        "a": 10,
        "b": 5,
        "c": 2,
    })


@pytest.fixture
def items_data():
    return json.dumps({
        "items": [
            {"name": "apple", "price": 3, "qty": 4},
            {"name": "banana", "price": 1, "qty": 10},
        ]
    })


# --- Basic path + path ---

@pytest.mark.parametrize("query,expected", [
    (".price + .tax", 120),
    (".a - .b", 5),
    (".a * .b", 50),
    (".a / .b", 2.0),
])
def test_basic_arithmetic(data, query, expected):
    assert rusty_jq.compile(query).first(data) == expected


# --- Path + literal ---

@pytest.mark.parametrize("query,expected", [
    (".price + 50", 150),
    (".price - 1", 99),
    (".price * 2", 200),
    (".price / 4", 25.0),
])
def test_arithmetic_with_literal(data, query, expected):
    assert rusty_jq.compile(query).first(data) == expected


# --- Int / float promotion ---

def test_int_float_promotion(data):
    # .price is int 100, .discount is float 0.1
    result = rusty_jq.compile(".price * .discount").first(data)
    assert abs(result - 10.0) < 1e-9


# --- Precedence: * binds tighter than + ---

def test_precedence_mul_before_add(data):
    # .a + .b * .c = 10 + (5 * 2) = 20, NOT (10 + 5) * 2 = 30
    assert rusty_jq.compile(".a + .b * .c").first(data) == 20


# --- Chained same-precedence (left-associative) ---

def test_chained_addition(data):
    # .a + .b + .c = (10 + 5) + 2 = 17
    assert rusty_jq.compile(".a + .b + .c").first(data) == 17


def test_chained_subtraction(data):
    # .a - .b - .c = (10 - 5) - 2 = 3
    assert rusty_jq.compile(".a - .b - .c").first(data) == 3


# --- Arithmetic in pipeline ---

def test_arithmetic_after_pipe(items_data):
    results = list(rusty_jq.compile(".items | .[] | .price * .qty").input(items_data))
    assert results == [12, 10]


# --- Arithmetic inside object constructor ---

def test_arithmetic_in_object(data):
    result = rusty_jq.compile("{total: .price + .tax}").first(data)
    assert result == {"total": 120}


# --- Arithmetic inside select condition ---

def test_arithmetic_in_select(items_data):
    results = list(rusty_jq.compile(
        ".items | .[] | select(.price * .qty > 10) | .name"
    ).input(items_data))
    assert results == ["apple"]


# --- Division produces float ---

def test_int_division_produces_float():
    data = json.dumps({"a": 7, "b": 2})
    result = rusty_jq.compile(".a / .b").first(data)
    assert result == 3.5
