import json
from datetime import timedelta

from dateutil.parser import parse


def flux_to_query():
    pass


def get_flux_query(bucket=None,
                   measurement=None,
                   device_id=None,
                   description=None,
                   time_range='1h',
                   time_from=None,
                   time_to=None,
                   aggregate_window=None,
                   aggregate_func=None,
                   sort=None,
                   count=None,
                   last=False,
                   limit=None,
                   drop_columns=None,
                   keep_only_columns=None,
                   group_columns=None,
                   distinct=False,
                   field='value',
                   bucket_to=None,
                   org_to=None,
                   time_shift=None,
                   time_offset: int = None
                   ) -> str:
    """

    :param time_offset:
    :param time_shift:
    :param field:
    :param bucket: Name of the bucket
    :param measurement: Name of the measurement
    :param device_id: ID of device
    :param description: Description of data set
    :param time_range:
    :param time_from:
    :param time_to:
    :param aggregate_window:
    :param aggregate_func:
    :param sort:
    :param count:
    :param last:
    :param limit:
    :param drop_columns:
    :param keep_only_columns:
    :param group_columns:
    :param distinct:
    :param bucket_to:
    :param org_to:
    :return:
    """
    if bucket is None:
        raise ValueError("Invalid value for `bucket`, must not be `None`")

    # if measurement is None:
    #     raise ValueError("Invalid value for `measurement`, must not be `None`")

    stop = None
    start = "-1h"

    if time_range is not None:
        start = f"-{time_range}"

    if time_from is not None:
        if type(time_from) == str:
            time_from = parse(time_from)
        if time_offset is not None:
            time_from = time_from - timedelta(hours=int(time_offset))
        start = time_from.strftime("%Y-%m-%dT%H:%M:%SZ")

    if time_to is not None:
        if type(time_to) == str:
            time_to = parse(time_to)
        if time_offset is not None:
            time_to = time_to - timedelta(hours=int(time_offset))
        stop = time_to.strftime("%Y-%m-%dT%H:%M:%SZ")

    if field is None:
        field = 'value'

    query_str = f'''from(bucket:"{bucket}")'''
    if stop:
        query_str += f'''\n\t|> range(start: {start}, stop:{stop})'''
    else:
        query_str += f'''\n\t|> range(start: {start})'''

    if measurement:
        query_str += f'''\n\t|> filter(fn: (r) => r["_measurement"] == "{measurement}")'''

    if device_id:
        query_str += f'''\n\t|> filter(fn: (r) => r["device_id"] == "{device_id}")'''

    if description:
        query_str += f'''\n\t|> filter(fn: (r) => r["description"] == "{description}")'''

    query_str += f'''\n\t|> filter(fn: (r) => r["_field"] == "{field}")'''

    if aggregate_window:
        if type(aggregate_window) != str:
            raise ValueError("Invalid type for `aggregate_window`, must be type `str`")
        if aggregate_func is None:
            aggregate_func = "mean"
        query_str += f'''\n\t|> aggregateWindow(every: {aggregate_window}, fn: {aggregate_func}, createEmpty: false)'''

    if group_columns:
        if type(group_columns) != list:
            raise ValueError("Invalid type for `group_columns`, must be type `list`")
        query_str += f'''\n\t|> group(columns: {json.dumps(group_columns)})'''

    if drop_columns:
        if type(drop_columns) != list:
            raise ValueError("Invalid type for `drop_columns`, must be type `list`")
        query_str += f'''\n\t|> drop(columns: {json.dumps(drop_columns)})'''

    if keep_only_columns:
        if type(keep_only_columns) != list:
            raise ValueError("Invalid type for `keep_only`, must be type `list`")
        query_str += f'''\n\t|> keep(columns: {json.dumps(keep_only_columns)})'''

    if count:
        query_str += f'''\n\t|> count(column: "_value")'''

    if sort is not None:
        # TODO: better sorting system
        # It appears that sort is very slow and cpu intensive
        if sort not in ['asc', 'desc']:
            raise ValueError("Invalid value for `sort`, must not be `asc` or `desc`")
        desc = "true" if sort == 'desc' else "false"
        query_str += f'''\n\t|> sort(columns: ["_time"], desc: {desc})'''

    if last:
        query_str += '''\n\t|> last()'''

    if limit is not None:
        query_str += f'''\n\t|> limit(n:{limit}, offset: 0)'''

    if distinct:
        query_str += f'''\n\t|> distinct()'''

    if time_shift is not None:
        query_str += f'''\n\t|> timeShift(duration: {time_shift})'''

    if bucket_to is not None and org_to is not None:
        query_str += f'''\n\t|> to(bucket: "{bucket_to}", org: "{org_to}")'''

    return query_str
