# -*- coding: utf-8 -*-
"""
This defines a basic set of data and the Star Wars Schema used for testing
and examples. It describes the major characters in the original
Star Wars trilogy.

NOTE: This may contain spoilers for the original Star Wars trilogy.

NOTE: The data is hard coded for the sake of the demo, but you could imagine
fetching this data from a backend service like SWApi rather than from hardcoded
JSON objects in a more complex demo.
"""

from py_gql.exc import ResolverError
from py_gql.schema import (
    Argument,
    EnumType,
    EnumValue,
    Field,
    InterfaceType,
    ListType,
    NonNullType,
    ObjectType,
    Schema,
    String,
)


luke = {
    "type": "Human",
    "id": "1000",
    "name": "Luke Skywalker",
    "friends": ["1002", "1003", "2000", "2001"],
    "appearsIn": [4, 5, 6],
    "homePlanet": "Tatooine",
}

vader = {
    "type": "Human",
    "id": "1001",
    "name": "Darth Vader",
    "friends": ["1004"],
    "appearsIn": [4, 5, 6],
    "homePlanet": "Tatooine",
}

han = {
    "type": "Human",
    "id": "1002",
    "name": "Han Solo",
    "friends": ["1000", "1003", "2001"],
    "appearsIn": [4, 5, 6],
}

leia = {
    "type": "Human",
    "id": "1003",
    "name": "Leia Organa",
    "friends": ["1000", "1002", "2000", "2001"],
    "appearsIn": [4, 5, 6],
    "homePlanet": "Alderaan",
}

tarkin = {
    "type": "Human",
    "id": "1004",
    "name": "Wilhuff Tarkin",
    "friends": ["1001"],
    "appearsIn": [4],
}

human_data = {
    "1000": luke,
    "1001": vader,
    "1002": han,
    "1003": leia,
    "1004": tarkin,
}

threepio = {
    "type": "Droid",
    "id": "2000",
    "name": "C-3PO",
    "friends": ["1000", "1002", "1003", "2001"],
    "appearsIn": [4, 5, 6],
    "primaryFunction": "Protocol",
}

artoo = {
    "type": "Droid",
    "id": "2001",
    "name": "R2-D2",
    "friends": ["1000", "1002", "1003"],
    "appearsIn": [4, 5, 6],
    "primaryFunction": "Astromech",
}

droid_data = {"2000": threepio, "2001": artoo}


def get_character(id_):
    return get_human(id_) or get_droid(id_)


def get_friends(character):
    return [get_character(f) for f in character["friends"]]


def get_hero(episode):
    if episode == 5:
        return luke
    return artoo


def get_human(id_):
    return human_data.get(id_)


def get_droid(id_):
    return droid_data.get(id_)


Episode = EnumType(
    "Episode",
    [
        EnumValue("NEWHOPE", 4, description="Released in 1977."),
        EnumValue("EMPIRE", 5, description="Released in 1980."),
        EnumValue("JEDI", 6, description="Released in 1983."),
    ],
    description="One of the films in the Star Wars Trilogy",
)


def resolve_character_type(character, *_):
    return {"Human": Human, "Droid": Droid}[character["type"]]


Character = InterfaceType(
    "Character",
    [
        Field(
            "id", NonNullType(String), description="The id of the character."
        ),
        Field("name", String, description="The name of the character."),
        Field(
            "friends",
            ListType(lambda: Character),
            description=(
                "The friends of the character, or an empty list if they have "
                "none."
            ),
        ),
        Field(
            "appearsIn",
            ListType(Episode),
            description="Which movies they appear in.",
        ),
        Field(
            "secretBackstory",
            String,
            description="All secrets about their past.",
        ),
    ],
    description="A character in the Star Wars Trilogy",
    resolve_type=resolve_character_type,
)  # type: InterfaceType


def resolve_secret_backstory(*args, **kwargs):
    raise ResolverError("secretBackstory is secret.", extensions={"code": 42})


Human = ObjectType(
    "Human",
    [
        Field("id", NonNullType(String), description="The id of the human."),
        Field("name", String, description="The name of the human."),
        Field(
            "friends",
            ListType(lambda: Character),
            description=(
                "The friends of the human, or an empty list if they have "
                "none."
            ),
            resolver=lambda human, *r, **k: get_friends(human),
        ),
        Field(
            "appearsIn",
            ListType(Episode),
            description="Which movies they appear in.",
        ),
        Field(
            "secretBackstory",
            String,
            description=(
                "Where are they from and how they came to be who they are."
            ),
            resolver=resolve_secret_backstory,
        ),
        Field(
            "homePlanet",
            String,
            description="The home planet of the human, or null if unknown.",
        ),
    ],
    description="A humanoid creature in the Star Wars universe.",
    interfaces=[Character],
)

Droid = ObjectType(
    "Droid",
    [
        Field("id", NonNullType(String), description="The id of the droid."),
        Field("name", String, description="The name of the droid."),
        Field(
            "friends",
            ListType(lambda: Character),
            description=(
                "The friends of the droid, or an empty list if they have "
                "none."
            ),
            resolver=lambda droid, *r, **k: get_friends(droid),
        ),
        Field(
            "appearsIn",
            ListType(Episode),
            description="Which movies they appear in.",
        ),
        Field(
            "secretBackstory",
            String,
            description=(
                "Where are they from and how they came to be who they are."
            ),
            resolver=resolve_secret_backstory,
        ),
        Field(
            "primaryFunction",
            String,
            description="The primary function of the droid.",
        ),
    ],
    description="A mechanical creature in the Star Wars universe.",
    interfaces=[Character],
)


Query = ObjectType(
    "Query",
    [
        Field(
            "hero",
            Character,
            [
                Argument(
                    "episode",
                    Episode,
                    description=(
                        "If omitted, returns the hero of the whole saga. If "
                        "provided, returns the hero of that particular episode."
                    ),
                )
            ],
            resolver=lambda *_, **args: get_hero(args.get("episode")),
        ),
        Field(
            "human",
            Human,
            [
                Argument(
                    "id", NonNullType(String), description="Id of the human"
                )
            ],
            resolver=lambda *_, **args: get_human(args.get("id")),
        ),
        Field(
            "droid",
            Droid,
            [
                Argument(
                    "id", NonNullType(String), description="Id of the droid"
                )
            ],
            resolver=lambda *_, **args: get_droid(args.get("id")),
        ),
    ],
)

StarWarsSchema = Schema(Query, types=[Human, Droid])
