"""Tests for scalar functions (string, list, type)."""

import pytest
import networkx as nx

from nxcypher import NXCypher


class TestStringFunctions:
    """Tests for string scalar functions."""

    def test_tolower(self):
        """TOLOWER converts string to lowercase."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", name="ALICE")
        host.add_node("Bob", name="BOB")
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->(b)
        RETURN TOLOWER(a.name), TOLOWER(b.name)
        """
        res = NXCypher(host).run(qry)
        assert res["TOLOWER(a.name)"] == ["alice"]
        assert res["TOLOWER(b.name)"] == ["bob"]

    def test_toupper(self):
        """TOUPPER converts string to uppercase."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", name="alice")
        host.add_node("Bob", name="bob")
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->(b)
        RETURN TOUPPER(a.name), TOUPPER(b.name)
        """
        res = NXCypher(host).run(qry)
        assert res["TOUPPER(a.name)"] == ["ALICE"]
        assert res["TOUPPER(b.name)"] == ["BOB"]

    def test_trim(self):
        """TRIM removes leading and trailing whitespace."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", name="  alice  ")
        host.add_node("Bob", name="\tbob\n")
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->(b)
        RETURN TRIM(a.name), TRIM(b.name)
        """
        res = NXCypher(host).run(qry)
        assert res["TRIM(a.name)"] == ["alice"]
        assert res["TRIM(b.name)"] == ["bob"]

    def test_string_function_with_none(self):
        """String functions handle None values."""
        host = nx.MultiDiGraph()
        host.add_node("Alice")  # No name property
        host.add_node("Bob", name="Bob")
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->(b)
        RETURN TOLOWER(a.name), TOLOWER(b.name)
        """
        res = NXCypher(host).run(qry)
        assert res["TOLOWER(a.name)"] == [None]
        assert res["TOLOWER(b.name)"] == ["bob"]


class TestListFunctions:
    """Tests for list scalar functions."""

    def test_size_string(self):
        """SIZE returns length of string."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", name="Alice")
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN SIZE(a.name)
        """
        res = NXCypher(host).run(qry)
        assert res["SIZE(a.name)"] == [5]

    def test_size_list(self):
        """SIZE returns length of list."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", tags=["python", "java", "go"])
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN SIZE(a.tags)
        """
        res = NXCypher(host).run(qry)
        assert res["SIZE(a.tags)"] == [3]

    def test_head(self):
        """HEAD returns first element of list."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", tags=["first", "second", "third"])
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN HEAD(a.tags)
        """
        res = NXCypher(host).run(qry)
        assert res["HEAD(a.tags)"] == ["first"]

    def test_tail(self):
        """TAIL returns all but first element."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", tags=["first", "second", "third"])
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN TAIL(a.tags)
        """
        res = NXCypher(host).run(qry)
        assert res["TAIL(a.tags)"] == [["second", "third"]]

    def test_head_empty_list(self):
        """HEAD of empty list returns None."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", tags=[])
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN HEAD(a.tags)
        """
        res = NXCypher(host).run(qry)
        assert res["HEAD(a.tags)"] == [None]


class TestTypeFunctions:
    """Tests for type introspection functions."""

    def test_labels(self):
        """LABELS returns node labels."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", __labels__={"Person", "Employee"}, name="Alice")
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN LABELS(a)
        """
        res = NXCypher(host).run(qry)
        assert set(res["LABELS(a)"][0]) == {"Person", "Employee"}

    def test_keys(self):
        """KEYS returns node property keys."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", name="Alice", age=30, city="NYC")
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN KEYS(a)
        """
        res = NXCypher(host).run(qry)
        keys = res["KEYS(a)"][0]
        # Should not include __labels__ or other internal attrs
        assert "name" in keys
        assert "age" in keys
        assert "city" in keys
        assert "__labels__" not in keys

    def test_labels_no_labels(self):
        """LABELS returns empty list for unlabeled node."""
        host = nx.MultiDiGraph()
        host.add_node("Alice", name="Alice")  # No __labels__
        host.add_edge("Alice", "Bob", __labels__={"KNOWS"})

        qry = """
        MATCH (a)-[:KNOWS]->()
        RETURN LABELS(a)
        """
        res = NXCypher(host).run(qry)
        assert res["LABELS(a)"] == [[]]
