# Copyright 2017-present Kensho Technologies, LLC.
"""Common test data and helper functions."""
from collections import namedtuple
from inspect import getmembers, isfunction
from pprint import pformat
import re
from typing import Dict, List, Optional, Set, Tuple, Union, cast
from unittest import TestCase

from graphql import GraphQLList, build_schema, lexicographic_sort_schema, print_schema
from graphql.type.definition import GraphQLInterfaceType, GraphQLObjectType, GraphQLUnionType
from graphql.type.schema import GraphQLSchema
from pyorient.orient import OrientDB
import six
import sqlalchemy
from sqlalchemy.dialects import mssql, postgresql

from ..compiler.compiler_entities import BasicBlock
from ..compiler.subclass import compute_subclass_sets
from ..debugging_utils import pretty_print_gremlin, pretty_print_match
from ..global_utils import is_same_type
from ..macros import MacroRegistry, create_macro_registry, register_macro_edge
from ..query_formatting.graphql_formatting import pretty_print_graphql
from ..schema import (
    ClassToFieldTypeOverridesType,
    GraphQLSchemaFieldType,
    TypeEquivalenceHintsType,
    is_vertex_field_name,
)
from ..schema.schema_info import (
    CommonSchemaInfo,
    CompositeJoinDescriptor,
    DirectJoinDescriptor,
    JoinDescriptor,
    SQLAlchemySchemaInfo,
    make_sqlalchemy_schema_info,
)
from ..schema_generation.orientdb import get_graphql_schema_from_orientdb_schema_data
from ..schema_generation.orientdb.schema_graph_builder import get_orientdb_schema_graph
from ..schema_generation.orientdb.utils import (
    ORIENTDB_INDEX_RECORDS_QUERY,
    ORIENTDB_SCHEMA_RECORDS_QUERY,
)
from ..schema_generation.schema_graph import SchemaGraph


# The strings which we will be comparing have newlines and spaces we'd like to get rid of,
# so we can compare expected and produced emitted code irrespective of whitespace.
WHITESPACE_PATTERN = re.compile("[\t\n ]*", flags=re.UNICODE)

# flag to indicate a test component should be skipped
SKIP_TEST = "SKIP"

# Text representation of a GraphQL schema generated from OrientDB.
# This schema isn't meant to be a paragon of good schema design.
# Instead, it aims to capture as many real-world edge cases as possible,
# without requiring a massive number of types and interfaces.
SCHEMA_TEXT = """
    schema {
        query: RootSchemaQuery
    }

    directive @filter(
        \"\"\"Name of the filter operation to perform.\"\"\"
        op_name: String!

        \"\"\"List of string operands for the operator.\"\"\"
        value: [String!]
    ) repeatable on FIELD | INLINE_FRAGMENT

    directive @tag(
        \"\"\"Name to apply to the given property field.\"\"\"
        tag_name: String!
    ) on FIELD

    directive @output(
        \"\"\"What to designate the output field generated from this property field.\"\"\"
        out_name: String!
    ) on FIELD

    directive @output_source on FIELD

    directive @optional on FIELD

    directive @recurse(
        \"\"\"
        Recurse up to this many times on this edge. A depth of 1 produces the current \
vertex and its immediate neighbors along the given edge.
        \"\"\"
        depth: Int!
    ) on FIELD

    directive @fold on FIELD

    directive @macro_edge on FIELD_DEFINITION

    directive @stitch(source_field: String!, sink_field: String!) on FIELD_DEFINITION

    type Animal implements Entity & UniquelyIdentifiable {
        _x_count: Int
        alias: [String]
        birthday: Date
        color: String
        description: String
        in_Animal_ParentOf: [Animal]
        in_Entity_Related: [Entity]
        name: String
        net_worth: Decimal
        out_Animal_BornAt: [BirthEvent]
        out_Animal_FedAt: [FeedingEvent]
        out_Animal_ImportantEvent: [Union__BirthEvent__Event__FeedingEvent]
        out_Animal_LivesIn: [Location]
        out_Animal_OfSpecies: [Species]
        out_Animal_ParentOf: [Animal]
        out_Entity_Related: [Entity]
        uuid: ID