# -*- coding: utf-8 -*-
"""
"""

from py_gql.lang import ast as _ast
from py_gql.lang.parser import parse
from py_gql.lang.visitor import SkipNode, Visitor, visit


class NullVisitor(Visitor):
    def enter(self, node):
        pass

    def leave(self, node):
        pass


def test_null_visitor_does_not_crash():
    ast = parse("{ a }", no_location=True)
    visit(NullVisitor(), ast)


def test_null_visitor_does_not_crash_on_kitchen_sink(fixture_file):
    source = fixture_file("kitchen-sink.graphql")
    ast = parse(source, no_location=True)
    visit(NullVisitor(), ast)


def test_null_visitor_does_not_crash_on_kitchen_sink_schema(fixture_file):
    source = fixture_file("schema-kitchen-sink.graphql")
    ast = parse(source, no_location=True)
    visit(NullVisitor(), ast)


class Tracker(NullVisitor):
    def __init__(self):
        self.stack = []

    def enter(self, node):
        self.stack.append(("enter", node.__class__.__name__))

    def leave(self, node):
        self.stack.append(("leave", node.__class__.__name__))


def test_it_processes_nodes_in_the_correct_order():
    ast = parse("{ a }", no_location=True)
    visitor = Tracker()
    visit(visitor, ast)
    assert visitor.stack == [
        ("enter", "Document"),
        ("enter", "OperationDefinition"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "OperationDefinition"),
        ("leave", "Document"),
    ]


def test_it_allows_early_exit():
    """ interrupt processing by raising """
    ast = parse("{ a { b { c { d { e } } } } }", no_location=True)

    class _Visitor(Tracker):
        def enter(self, node):
            super(_Visitor, self).enter(node)
            if isinstance(node, _ast.Field) and node.name.value == "b":
                raise SkipNode()

    visitor = _Visitor()
    visit(visitor, ast)

    assert visitor.stack == [
        ("enter", "Document"),
        ("enter", "OperationDefinition"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "OperationDefinition"),
        ("leave", "Document"),
    ]


def test_it_processes_kitchen_sink(fixture_file):
    ks = fixture_file("kitchen-sink.graphql")
    visitor = Tracker()
    visit(visitor, parse(ks, no_location=True))
    assert visitor.stack == [
        ("enter", "Document"),
        ("enter", "OperationDefinition"),
        ("enter", "VariableDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "VariableDefinition"),
        ("enter", "VariableDefinition"),
        ("enter", "EnumValue"),
        ("leave", "EnumValue"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "VariableDefinition"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "Argument"),
        ("enter", "ListValue"),
        ("enter", "IntValue"),
        ("leave", "IntValue"),
        ("enter", "IntValue"),
        ("leave", "IntValue"),
        ("leave", "ListValue"),
        ("leave", "Argument"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("enter", "InlineFragment"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("enter", "Field"),
        ("enter", "Argument"),
        ("enter", "IntValue"),
        ("leave", "IntValue"),
        ("leave", "Argument"),
        ("enter", "Argument"),
        ("enter", "Variable"),
        ("leave", "Variable"),
        ("leave", "Argument"),
        ("enter", "Directive"),
        ("enter", "Argument"),
        ("enter", "Variable"),
        ("leave", "Variable"),
        ("leave", "Argument"),
        ("leave", "Directive"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("enter", "FragmentSpread"),
        ("leave", "FragmentSpread"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "InlineFragment"),
        ("enter", "InlineFragment"),
        ("enter", "Directive"),
        ("enter", "Argument"),
        ("enter", "Variable"),
        ("leave", "Variable"),
        ("leave", "Argument"),
        ("leave", "Directive"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "InlineFragment"),
        ("enter", "InlineFragment"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "InlineFragment"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "OperationDefinition"),
        ("enter", "OperationDefinition"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "Argument"),
        ("enter", "IntValue"),
        ("leave", "IntValue"),
        ("leave", "Argument"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "OperationDefinition"),
        ("enter", "OperationDefinition"),
        ("enter", "VariableDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "VariableDefinition"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "Argument"),
        ("enter", "Variable"),
        ("leave", "Variable"),
        ("leave", "Argument"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("enter", "Field"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "OperationDefinition"),
        ("enter", "FragmentDefinition"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "Argument"),
        ("enter", "Variable"),
        ("leave", "Variable"),
        ("leave", "Argument"),
        ("enter", "Argument"),
        ("enter", "Variable"),
        ("leave", "Variable"),
        ("leave", "Argument"),
        ("enter", "Argument"),
        ("enter", "ObjectValue"),
        ("enter", "ObjectField"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("leave", "ObjectField"),
        ("enter", "ObjectField"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("leave", "ObjectField"),
        ("leave", "ObjectValue"),
        ("leave", "Argument"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "FragmentDefinition"),
        ("enter", "OperationDefinition"),
        ("enter", "SelectionSet"),
        ("enter", "Field"),
        ("enter", "Argument"),
        ("enter", "BooleanValue"),
        ("leave", "BooleanValue"),
        ("leave", "Argument"),
        ("enter", "Argument"),
        ("enter", "BooleanValue"),
        ("leave", "BooleanValue"),
        ("leave", "Argument"),
        ("enter", "Argument"),
        ("enter", "NullValue"),
        ("leave", "NullValue"),
        ("leave", "Argument"),
        ("leave", "Field"),
        ("enter", "Field"),
        ("leave", "Field"),
        ("leave", "SelectionSet"),
        ("leave", "OperationDefinition"),
        ("leave", "Document"),
    ]


def test_it_processes_schema_kitchen_sink(fixture_file):
    ks = fixture_file("schema-kitchen-sink.graphql")
    visitor = Tracker()
    visit(visitor, parse(ks, no_location=True))
    assert visitor.stack == [
        ("enter", "Document"),
        ("enter", "SchemaDefinition"),
        ("enter", "OperationTypeDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "OperationTypeDefinition"),
        ("enter", "OperationTypeDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "OperationTypeDefinition"),
        ("leave", "SchemaDefinition"),
        ("enter", "ObjectTypeDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "FieldDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NonNullType"),
        ("leave", "NonNullType"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "InputValueDefinition"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "ListType"),
        ("leave", "ListType"),
        ("enter", "ListValue"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("leave", "ListValue"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "ObjectValue"),
        ("enter", "ObjectField"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("leave", "ObjectField"),
        ("leave", "ObjectValue"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "NullValue"),
        ("leave", "NullValue"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("leave", "ObjectTypeDefinition"),
        ("enter", "ObjectTypeDefinition"),
        ("enter", "Directive"),
        ("enter", "Argument"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("leave", "Argument"),
        ("leave", "Directive"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "InputValueDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "FieldDefinition"),
        ("leave", "ObjectTypeDefinition"),
        ("enter", "ObjectTypeDefinition"),
        ("leave", "ObjectTypeDefinition"),
        ("enter", "ObjectTypeExtension"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "ListType"),
        ("leave", "ListType"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("leave", "ObjectTypeExtension"),
        ("enter", "ObjectTypeExtension"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "ObjectTypeExtension"),
        ("enter", "InterfaceTypeDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "FieldDefinition"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "StringValue"),
        ("leave", "StringValue"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("leave", "InterfaceTypeDefinition"),
        ("enter", "InterfaceTypeDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "InputValueDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "FieldDefinition"),
        ("leave", "InterfaceTypeDefinition"),
        ("enter", "InterfaceTypeDefinition"),
        ("leave", "InterfaceTypeDefinition"),
        ("enter", "InterfaceTypeExtension"),
        ("enter", "FieldDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "InputValueDefinition"),
        ("enter", "NonNullType"),
        ("leave", "NonNullType"),
        ("leave", "InputValueDefinition"),
        ("leave", "FieldDefinition"),
        ("leave", "InterfaceTypeExtension"),
        ("enter", "InterfaceTypeExtension"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "InterfaceTypeExtension"),
        ("enter", "UnionTypeDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "UnionTypeDefinition"),
        ("enter", "UnionTypeDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "UnionTypeDefinition"),
        ("enter", "UnionTypeDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "UnionTypeDefinition"),
        ("enter", "UnionTypeDefinition"),
        ("leave", "UnionTypeDefinition"),
        ("enter", "UnionTypeExtension"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "UnionTypeExtension"),
        ("enter", "UnionTypeExtension"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "UnionTypeExtension"),
        ("enter", "ScalarTypeDefinition"),
        ("leave", "ScalarTypeDefinition"),
        ("enter", "ScalarTypeDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "ScalarTypeDefinition"),
        ("enter", "ScalarTypeExtension"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "ScalarTypeExtension"),
        ("enter", "EnumTypeDefinition"),
        ("enter", "EnumValueDefinition"),
        ("leave", "EnumValueDefinition"),
        ("enter", "EnumValueDefinition"),
        ("leave", "EnumValueDefinition"),
        ("leave", "EnumTypeDefinition"),
        ("enter", "EnumTypeDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("enter", "EnumValueDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "EnumValueDefinition"),
        ("enter", "EnumValueDefinition"),
        ("leave", "EnumValueDefinition"),
        ("leave", "EnumTypeDefinition"),
        ("enter", "EnumTypeDefinition"),
        ("leave", "EnumTypeDefinition"),
        ("enter", "EnumTypeExtension"),
        ("enter", "EnumValueDefinition"),
        ("leave", "EnumValueDefinition"),
        ("leave", "EnumTypeExtension"),
        ("enter", "EnumTypeExtension"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "EnumTypeExtension"),
        ("enter", "InputObjectTypeDefinition"),
        ("enter", "InputValueDefinition"),
        ("enter", "NonNullType"),
        ("leave", "NonNullType"),
        ("leave", "InputValueDefinition"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "IntValue"),
        ("leave", "IntValue"),
        ("leave", "InputValueDefinition"),
        ("leave", "InputObjectTypeDefinition"),
        ("enter", "InputObjectTypeDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "InputValueDefinition"),
        ("leave", "InputObjectTypeDefinition"),
        ("enter", "InputObjectTypeDefinition"),
        ("leave", "InputObjectTypeDefinition"),
        ("enter", "InputObjectTypeExtension"),
        ("enter", "InputValueDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("enter", "FloatValue"),
        ("leave", "FloatValue"),
        ("leave", "InputValueDefinition"),
        ("leave", "InputObjectTypeExtension"),
        ("enter", "InputObjectTypeExtension"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "InputObjectTypeExtension"),
        ("enter", "DirectiveDefinition"),
        ("enter", "InputValueDefinition"),
        ("enter", "NonNullType"),
        ("leave", "NonNullType"),
        ("leave", "InputValueDefinition"),
        ("leave", "DirectiveDefinition"),
        ("enter", "DirectiveDefinition"),
        ("enter", "InputValueDefinition"),
        ("enter", "NonNullType"),
        ("leave", "NonNullType"),
        ("leave", "InputValueDefinition"),
        ("leave", "DirectiveDefinition"),
        ("enter", "DirectiveDefinition"),
        ("enter", "InputValueDefinition"),
        ("enter", "NonNullType"),
        ("leave", "NonNullType"),
        ("leave", "InputValueDefinition"),
        ("leave", "DirectiveDefinition"),
        ("enter", "SchemaExtension"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "SchemaExtension"),
        ("enter", "SchemaExtension"),
        ("enter", "OperationTypeDefinition"),
        ("enter", "NamedType"),
        ("leave", "NamedType"),
        ("leave", "OperationTypeDefinition"),
        ("enter", "Directive"),
        ("leave", "Directive"),
        ("leave", "SchemaExtension"),
        ("leave", "Document"),
    ]
