"""Tests for the agtype parser."""

from decimal import Decimal
from math import isnan

import pytest

from piggie._agtype.loader import parse_agtype
from piggie.exceptions import AgtypeParseError
from piggie.types import Edge, Path, Vertex


class TestScalarParsing:
    def test_string(self):
        assert parse_agtype('"hello"') == "hello"

    def test_integer(self):
        assert parse_agtype("42") == 42

    def test_negative_integer(self):
        assert parse_agtype("-7") == -7

    def test_float(self):
        assert parse_agtype("3.14") == pytest.approx(3.14)

    def test_exponent_float(self):
        assert parse_agtype("1.5e10") == pytest.approx(1.5e10)

    def test_true(self):
        assert parse_agtype("true") is True

    def test_false(self):
        assert parse_agtype("false") is False

    def test_null(self):
        assert parse_agtype("null") is None

    def test_none_input(self):
        assert parse_agtype("") is None

    def test_nan(self):
        result = parse_agtype("NaN::float")
        assert isnan(result)

    def test_infinity(self):
        assert parse_agtype("Infinity::float") == float("inf")

    def test_negative_infinity(self):
        assert parse_agtype("-Infinity::float") == float("-inf")

    def test_numeric(self):
        result = parse_agtype("123.456::numeric")
        assert result == Decimal("123.456")


class TestCollectionParsing:
    def test_array(self):
        assert parse_agtype("[1, 2, 3]") == [1, 2, 3]

    def test_empty_array(self):
        assert parse_agtype("[]") == []

    def test_object(self):
        result = parse_agtype('{"name": "Alice", "age": 30}')
        assert result == {"name": "Alice", "age": 30}

    def test_nested_object(self):
        result = parse_agtype('{"a": {"b": 1}}')
        assert result == {"a": {"b": 1}}


class TestVertexParsing:
    def test_vertex(self):
        agtype = '{"id": 1125899906842625, "label": "Person", "properties": {"name": "Alice", "age": 30}}::vertex'
        result = parse_agtype(agtype)
        assert isinstance(result, Vertex)
        assert result.id == 1125899906842625
        assert result.label == "Person"
        assert result.properties == {"name": "Alice", "age": 30}

    def test_vertex_empty_properties(self):
        agtype = '{"id": 100, "label": "Node", "properties": {}}::vertex'
        result = parse_agtype(agtype)
        assert isinstance(result, Vertex)
        assert result.properties == {}


class TestEdgeParsing:
    def test_edge(self):
        agtype = (
            '{"id": 1688849860263937, "label": "KNOWS", '
            '"end_id": 1125899906842626, "start_id": 1125899906842625, '
            '"properties": {"since": 2020}}::edge'
        )
        result = parse_agtype(agtype)
        assert isinstance(result, Edge)
        assert result.id == 1688849860263937
        assert result.label == "KNOWS"
        assert result.start_id == 1125899906842625
        assert result.end_id == 1125899906842626
        assert result.properties == {"since": 2020}


class TestPathParsing:
    def test_path(self):
        agtype = (
            '[{"id": 1, "label": "A", "properties": {}}::vertex, '
            '{"id": 10, "label": "R", "end_id": 2, "start_id": 1, '
            '"properties": {}}::edge, '
            '{"id": 2, "label": "B", "properties": {}}::vertex]::path'
        )
        result = parse_agtype(agtype)
        assert isinstance(result, Path)
        assert len(result) == 3
        assert isinstance(result[0], Vertex)
        assert isinstance(result[1], Edge)
        assert isinstance(result[2], Vertex)


class TestErrorHandling:
    def test_invalid_agtype(self):
        with pytest.raises(AgtypeParseError):
            parse_agtype("not valid agtype {{{")
