# external imports
from graphql import parse
# local imports
from .walk_query import walk_query

async def parse_string(query, resolver, connection_resolver, mutation_resolver, extra_mutations={}, current_user=None, obey_auth=True):
    # start off with an empty dictionary
    result = {}
    # collect the errors in a list
    errors = []

    # parse the query
    query_parsed = parse(query)

    # the query operations contained in the string
    queries = [operation for operation in query_parsed.definitions \
                                    if operation.operation == 'query']
    # if there are queries to run
    if queries:
        # start off with an empty dictionary
        query_result = {}

        # TODO: handle multiple queries per string
        # grab the first query with no name
        query = [query for query in queries if not query.name][0]

        # go to each selection set of the query
        for selection in query.selection_set.selections:
            # walk the selection and add it to the result
            query_result[selection.name.value] = await walk_query(
                selection,
                resolver,
                connection_resolver,
                errors,
                obey_auth=obey_auth,
                current_user=current_user
            )

        # add the query result to the final result
        result['data'] = query_result

    # the mutations contained in the query
    mutations = [operation for operation in query_parsed.definitions
                                    if operation.operation == 'mutation']

    # store the result of all mutations
    mutations_result = {}
    # we need to execute each mutation within each mutation operation
    for mutation_set in mutations:
        for mutation in mutation_set.selection_set.selections:
            # the name of the mutation
            mutation_name = mutation.name.value
            # the args of the query
            mutation_args = {arg.name.value: arg.value.value for arg in mutation.arguments}
            # the requested fields
            mutation_selections = mutation.selection_set.selections
            # the fields are treated as a list of fields to be passed along
            mutation_fields = [field.name.value for field in mutation_selections]

            # pass the necessary information to the mutation resolver
            try:
                # if the mutation is a custom one
                if mutation_name in extra_mutations:
                    # call the mutation
                    custom_mut_result = await extra_mutations[mutation_name](**mutation_args)
                    # build a dictionary of the fields we need
                    mut_result = {field : custom_mut_result[field] for field in mutation_fields}

                # otherwise its not a mutation with special handling
                else:
                    # pass the various mutation parameters to the resolver
                    mut_result = await mutation_resolver(
                        mutation_name,
                        mutation_args,
                        mutation_fields
                    )

                # if there is only one field and it is an object
                # TODO: clean this up!!!!
                #       support more than just one nested object (see GH for discussion)
                if len(mutation_selections) == 1 and \
                        mutation_selections[0].selection_set and \
                        len(mutation_selections[0].selection_set.selections) > 0:
                    # grab the only mutation field so we can nest appropriately
                    mutations_result[mutation_name] = {
                        mutation_selections[0].name.value: mut_result
                    }
                # otherwise
                else:
                    mutations_result[mutation_name] = mut_result
            # if something goes wrong
            except Exception as e :
                # add the error to the list
                errors.append(str(e))


    # if any mutations were performed
    if mutations_result:
        # add the result to the final summary
        result['mutation'] = mutations_result


    # return the final result
    return {
        'errors': errors,
        **result
    }
