"""
Connection
==========
"""

import pathlib

from gql import Client, gql
from gql.dsl import DSLField, DSLMutation, DSLQuery, DSLSchema, dsl_gql
from gql.transport.requests import RequestsHTTPTransport

from deepcrawl_graphql.exceptions import MissingCredentials

ENDPOINT = "https://graph.deepcrawl.com/"
SCHMEA_PATH = pathlib.Path(__file__).parent.resolve() / "deepcrawl_schema.graphql"
with open(SCHMEA_PATH, "r", encoding="utf-8") as f:
    SCHEMA = f.read()


class DeepCrawlConnection:
    """DeepCrawlConnection class

    Creates a connection instance used for sending GraphQL queries to DeepCrawl.

    >>> from deepcrawl_graphql.api import DeepCrawlConnection

    >>> DeepCrawlConnection("user_key_id", "secret")
    DeepCrawlConnection instance

    >>> DeepCrawlConnection(token="token")
    DeepCrawlConnection instance

    :param user_key_id: user key, used together with secret for authentication.
    :type user_key_id: int or str
    :param secret: secret key, used together with user_key_id for authentication.
    :type secret: str
    :param token: authentication token, if used ignores the user_key_id and secret
    :type token: str
    """

    def __init__(self, user_key_id=None, secret=None, token=None) -> None:
        if not token:
            if not user_key_id or not secret:
                raise MissingCredentials("Eighter token or user_key_id and secret pair must be provided")
            token = self._get_token(user_key_id, secret)

        self.headers = {"x-auth-token": token}
        self.transport = RequestsHTTPTransport(url=ENDPOINT, headers=self.headers)
        self.client = Client(transport=self.transport, schema=SCHEMA)
        self.ds = DSLSchema(self.client.schema)

    def __str__(self) -> str:
        return "DeepCrawlConnection instance"

    def __repr__(self) -> str:
        return "DeepCrawlConnection instance"

    def run_query(self, query):
        """Runs a query.

        >>> conn = DeepCrawlConnection("user_key_id", "secret")

        There are 3 possible ways you can run a query

        Using a query string. You can use the DeepCrawl explorer to construct the string https://graph-docs.deepcrawl.com/graphql/explorer

        >>> query = 'query MyQuery {version}'
        >>> conn.run_query(query)
        {'version': '1.91.1-next.0-en'}

        Using the gql package to construct a dynamic query.

        >>> from gql.dsl import DSLQuery
        >>> query = DSLQuery(conn.ds.Query.me.select(conn.ds.User.id, conn.ds.User.username))
        >>> conn.run_query(query)
        {
            'me': {
                'id': 'id',
                'username': 'email@example.com'
            }
        }
        # For more information about constructing queries with dsl
        # see https://gql.readthedocs.io/en/stable/advanced/dsl_module.html

        Import a query from the deepcrawl_graphql package and use it's prebuild queries.

        >>> from deepcrawl_graphql.me.me import MeQuery
        >>> me_query = MeQuery(conn)
        >>> me_query.select_me()
        >>> conn.run_query(me_query)
        {
            'me': {
                'id': 'id',
                'username': 'email@example.com',
                'email': 'email@example.com',
                'firstName': 'FirstName',
                'lastName': 'LastName',
                'createdAt': '2019-10-27T17:11:17.000Z',
                'updatedAt': '2022-01-15T10:10:38.000Z',
                'jobTitle': None,
                'overallLimitLevelsMax': 1000,
                'overallLimitPagesMax': 10000000,
                'ssoClientId': None,
                'termsAgreed': True,
                'rawID': 'id',
                'permissions': []
            }
        }


        :param query: query object
        :type query: str or Query
        """
        from deepcrawl_graphql.query import Query  # pylint: disable=import-outside-toplevel

        if isinstance(query, Query):
            query = dsl_gql(DSLQuery(query.query))
        if isinstance(query, DSLField):
            query = dsl_gql(DSLQuery(query))
        elif isinstance(query, DSLQuery):
            query = dsl_gql(query)
        elif isinstance(query, str):
            query = gql(query)
        return self.client.execute(query)

    @staticmethod
    def _get_token(user_key_id, secret) -> str:
        transport = RequestsHTTPTransport(url=ENDPOINT)
        client = Client(transport=transport, schema=SCHEMA)
        ds = DSLSchema(client.schema)
        query = dsl_gql(
            DSLMutation(
                ds.Mutation.createSessionUsingUserKey.args(
                    input={
                        "userKeyId": user_key_id,
                        "secret": secret,
                    }
                ).select(ds.Session.token)
            )
        )
        response = client.execute(query)
        return response.get("createSessionUsingUserKey", {}).get("token")
