"""Tests for extra pretty printers (generated by extra_pretty_gen.py).

These cover proto messages and enums that don't have grammar-based printers
(tokens, specials, generics, enums), as well as oneof-only messages that
should pass through to the inner variant without wrapping.
"""

from lqp.gen.pretty import PrettyPrinter
from lqp.proto.v1 import fragments_pb2, logic_pb2, transactions_pb2


def _pp(msg) -> str:
    """Dispatch a single message through the pretty printer and return output."""
    pp = PrettyPrinter()
    pp.pprint_dispatch(msg)
    return pp.get_output()


# ---------------------------------------------------------------------------
# Token printers
# ---------------------------------------------------------------------------


class TestTokenPrinters:
    def test_decimal_value(self):
        msg = logic_pb2.DecimalValue(
            value=logic_pb2.Int128Value(low=123456789, high=0),
            precision=18,
            scale=6,
        )
        assert _pp(msg) == "123.456789d18"

    def test_int128_value(self):
        msg = logic_pb2.Int128Value(low=42, high=0)
        assert _pp(msg) == "42i128"

    def test_int128_value_negative(self):
        msg = logic_pb2.Int128Value(low=0xFFFFFFFFFFFFFFFF, high=0xFFFFFFFFFFFFFFFF)
        assert _pp(msg) == "-1i128"

    def test_uint128_value(self):
        msg = logic_pb2.UInt128Value(low=0xFF, high=0)
        assert _pp(msg) == "0xff"

    def test_uint128_value_zero(self):
        msg = logic_pb2.UInt128Value(low=0, high=0)
        assert _pp(msg) == "0x0"


# ---------------------------------------------------------------------------
# Special printers
# ---------------------------------------------------------------------------


class TestSpecialPrinters:
    def test_missing_value(self):
        assert _pp(logic_pb2.MissingValue()) == "missing"

    def test_debug_info_empty(self):
        msg = fragments_pb2.DebugInfo()
        assert _pp(msg) == "(debug_info)"

    def test_debug_info_one_entry(self):
        msg = fragments_pb2.DebugInfo(
            ids=[logic_pb2.RelationId(id_low=1, id_high=0)],
            orig_names=["my_rel"],
        )
        assert _pp(msg) == '(debug_info\n  (0x1 "my_rel"))'


# ---------------------------------------------------------------------------
# Generic printers (scalar fields, nested enum, oneof)
# ---------------------------------------------------------------------------


class TestGenericPrinters:
    def test_be_tree_config(self):
        msg = logic_pb2.BeTreeConfig(
            epsilon=0.5,
            max_pivots=128,
            max_deltas=256,
            max_leaf=512,
        )
        expected = (
            "(be_tree_config\n"
            "  :epsilon 0.5\n"
            "  :max_pivots 128\n"
            "  :max_deltas 256\n"
            "  :max_leaf 512)"
        )
        assert _pp(msg) == expected

    def test_ivm_config_auto(self):
        msg = transactions_pb2.IVMConfig(
            level=transactions_pb2.MaintenanceLevel.MAINTENANCE_LEVEL_AUTO,
        )
        assert _pp(msg) == "(ivm_config\n  :level auto)"

    def test_ivm_config_off(self):
        msg = transactions_pb2.IVMConfig(
            level=transactions_pb2.MaintenanceLevel.MAINTENANCE_LEVEL_OFF,
        )
        assert _pp(msg) == "(ivm_config\n  :level off)"

    def test_be_tree_locator_root_pageid(self):
        msg = logic_pb2.BeTreeLocator(
            root_pageid=logic_pb2.UInt128Value(low=42, high=0),
            element_count=100,
            tree_height=3,
        )
        expected = (
            "(be_tree_locator\n"
            "  :element_count 100\n"
            "  :tree_height 3\n"
            "  :location (:root_pageid 0x2a))"
        )
        assert _pp(msg) == expected

    def test_be_tree_locator_inline_data(self):
        msg = logic_pb2.BeTreeLocator(
            inline_data=b"\xab\xcd",
            element_count=10,
            tree_height=1,
        )
        expected = (
            "(be_tree_locator\n"
            "  :element_count 10\n"
            "  :tree_height 1\n"
            "  :location (:inline_data 0xabcd))"
        )
        assert _pp(msg) == expected

    def test_be_tree_locator_no_location(self):
        msg = logic_pb2.BeTreeLocator(
            element_count=0,
            tree_height=0,
        )
        expected = (
            "(be_tree_locator\n"
            "  :element_count 0\n"
            "  :tree_height 0\n"
            "  :location nothing)"
        )
        assert _pp(msg) == expected


# ---------------------------------------------------------------------------
# Enum printer
# ---------------------------------------------------------------------------


class TestEnumPrinter:
    def test_maintenance_level_unspecified(self):
        pp = PrettyPrinter()
        pp.pretty_maintenance_level(
            transactions_pb2.MaintenanceLevel.MAINTENANCE_LEVEL_UNSPECIFIED
        )
        assert pp.get_output() == "unspecified"

    def test_maintenance_level_off(self):
        pp = PrettyPrinter()
        pp.pretty_maintenance_level(
            transactions_pb2.MaintenanceLevel.MAINTENANCE_LEVEL_OFF
        )
        assert pp.get_output() == "off"

    def test_maintenance_level_auto(self):
        pp = PrettyPrinter()
        pp.pretty_maintenance_level(
            transactions_pb2.MaintenanceLevel.MAINTENANCE_LEVEL_AUTO
        )
        assert pp.get_output() == "auto"

    def test_maintenance_level_all(self):
        pp = PrettyPrinter()
        pp.pretty_maintenance_level(
            transactions_pb2.MaintenanceLevel.MAINTENANCE_LEVEL_ALL
        )
        assert pp.get_output() == "all"


# ---------------------------------------------------------------------------
# Oneof-only messages (should print inner variant without wrapping)
# ---------------------------------------------------------------------------


class TestOneofOnlyMessages:
    def test_term_var(self):
        msg = logic_pb2.Term(var=logic_pb2.Var(name="x"))
        assert _pp(msg) == "x"

    def test_term_constant_int(self):
        msg = logic_pb2.Term(constant=logic_pb2.Value(int_value=42))
        assert _pp(msg) == "42"

    def test_value_int(self):
        msg = logic_pb2.Value(int_value=99)
        assert _pp(msg) == "99"

    def test_value_string(self):
        msg = logic_pb2.Value(string_value="hello")
        assert _pp(msg) == '"hello"'

    def test_value_missing(self):
        msg = logic_pb2.Value(missing_value=logic_pb2.MissingValue())
        assert _pp(msg) == "missing"

    def test_monoid_or(self):
        msg = logic_pb2.Monoid(or_monoid=logic_pb2.OrMonoid())
        assert _pp(msg) == "(or)"

    def test_instruction_assign(self):
        msg = logic_pb2.Instruction(
            assign=logic_pb2.Assign(
                name=logic_pb2.RelationId(id_low=1, id_high=0),
                body=logic_pb2.Abstraction(
                    value=logic_pb2.Formula(conjunction=logic_pb2.Conjunction(args=[])),
                ),
            )
        )
        result = _pp(msg)
        assert result.startswith("(assign")

    def test_data_rel_edb(self):
        msg = logic_pb2.Data(
            rel_edb=logic_pb2.RelEDB(
                target_id=logic_pb2.RelationId(id_low=1, id_high=0),
                path=["base", "rel"],
                types=[],
            )
        )
        result = _pp(msg)
        assert result.startswith("(rel_edb")

    def test_read_demand(self):
        msg = transactions_pb2.Read(
            demand=transactions_pb2.Demand(
                relation_id=logic_pb2.RelationId(id_low=1, id_high=0),
            )
        )
        result = _pp(msg)
        assert result.startswith("(demand")
