import gzip
from urllib.parse import urlencode
from influxed.mediator.influx_client import influx_client
from influxed.ifql.statement import a_statement
from influxed.ifql.statement import dummy_statement
from influxed.ifql.select_statement import select_statement_builder
from influxed.ifql.show_statement import show_statement_builder
from influxed.ifql.insert_statement import insert_statement_builder

class Mediator(object):

    def __init__(self):
        self.client_map = {}
        self.number_of_clients = 0

    def add_client(self, connection_string, username, password, **kwargs):
        if(not connection_string or not username):
            raise TypeError(f'Type error connection_sting {type(connection_string)} and username {type(username)} must be <str>')
        
        if(connection_string+username not in self.client_map):
            self.client_map[connection_string+username] = influx_client(
                connection_string,
                username, password,
                **kwargs
            )
            self.number_of_clients += 1

    def query_to_body(self, query):
        if(issubclass(type(query), a_statement)):
            return self.a_statement_query_to_body(query)
        if(isinstance(query, insert_statement_builder)):
            return self.instert_statement_to_body(query)
        return self.a_statement_query_to_body(query)

    def instert_statement_to_body(self, query):
        bodies = self.split_request(query.format())
        bodies = [gzip.compress(b.encode()) for b in bodies]
        return [(f'/write?db={query.database }', b) for b in bodies]

    def split_request(self, body):
        """
            Recursive function!
            Devides a request into too if it is too long.
        """
        if(len(body.encode('utf-8')) > 10000000):
            # Size is exeeding the max allowed size!
            body_parts = body.split('\n')
            half = int(len(body_parts)/2)
            b0 = '\n'.join(body_parts[:half])
            b1 = '\n'.join(body_parts[half:])
            return self.split_request(b0) + self.split_request(b1)
        return [body]


    def a_statement_query_to_body(self, query):
        param = {
            'q': query.format(),
            'chunked': 'true',
        }
        if(query.database):
            param['db'] = query.database
        return [('/query', urlencode(param))]


    def get_client(self, connection_string, username):
        client = None
        if((connection_string is None or username is None) and self.number_of_clients != 1):
            raise Exception('No connection_string and username specified')
        elif(connection_string is None or username is None):
            client = self.client_map[next(iter(self.client_map))]
        else:
            client = self.client_map[connection_string+username]
        return client

    def execute(self, query, connection_string=None, username=None, database=None):
        if(isinstance(query, str)):
            query = dummy_statement(query, database)
        
        query_list = self.query_to_body(query)

        if(len(query_list) == 1):
            return self.get_client(connection_string, username).fetch(*query_list[0])

        return [
            self.get_client(connection_string, username).fetch(*query_list[i])
            for i in range(len(query_list))
        ]

mediator = Mediator()