import decimal

from django.template.defaultfilters import date as date_format


def get_history_data(queryset):
    resources = {}
    needs_aggregation = False
    for rdata in queryset:
        resource = rdata.resource
        try:
            (options, aggregation) = resources[resource]
        except KeyError:
            aggregation = resource.aggregation_instance
            options = {
                'aggregation': str(aggregation.verbose_name),
                'aggregated_history': aggregation.aggregated_history,
                'content_type': rdata.content_type.model,
                'content_object': rdata.content_object_repr,
                'unit': resource.unit,
                'scale': resource.get_scale(),
                'verbose_name': str(resource.verbose_name),
                'dates': set() if aggregation.aggregated_history else None,
                'objects': [],
            }
            resources[resource] = (options, aggregation)
        if aggregation.aggregated_history:
            needs_aggregation = True
        monitors = []
        scale = options['scale']
        all_dates = options['dates']
        for monitor_name, dataset in rdata.get_monitor_datasets():
            datasets = {}
            for content_object, datas in aggregation.aggregate_history(dataset):
                if aggregation.aggregated_history:
                    serie = {}
                    for data in datas:
                        value = round(float(data.value)/scale, 3) if data.value is not None else None
                        all_dates.add(data.date)
                        serie[data.date] = value
                else:
                    serie = []
                    for data in datas:
                        date = data.created_at.timestamp()
                        date = int(str(date).split('.')[0] + '000')
                        value = round(float(data.value)/scale, 3) if data.value is not None else None
                        serie.append(
                            (date, value)
                        )
                datasets[content_object] = serie
            monitors.append({
                'name': monitor_name,
                'datasets': datasets,
            })
        options['objects'].append({
            'object_name': rdata.content_object_repr,
            'current': round(float(rdata.used or 0), 3),
            'allocated': float(rdata.allocated) if rdata.allocated is not None else None,
            'updated_at': rdata.updated_at.isoformat() if rdata.updated_at else None,
            'monitors': monitors,
        })
    if needs_aggregation:
        result = []
        for options, aggregation in resources.values():
            if aggregation.aggregated_history:
                all_dates = sorted(options['dates'])
                options['dates'] = [date_format(date) for date in all_dates]
                for obj in options['objects']:
                    for monitor in obj['monitors']:
                        series = []
                        for content_object, dataset in monitor['datasets'].items():
                            data = []
                            for date in all_dates:
                                data.append(dataset.get(date, 0.0))
                            series.append({
                                'name': content_object,
                                'data': data,
                            })
                        monitor['datasets'] = series
            result.append(options)
    else:
        result = [resource[0] for resource in resources.values()]
    return result


def delete_old_equal_values(dataset):
    """ only first and last values of an equal serie (+-error) are kept """
    prev_value = None
    prev_key = None
    delete_count = 0
    error = decimal.Decimal('0.005')
    third = False
    for mdata in dataset.order_by('content_type_id', 'object_id', 'created_at'):
        key = (mdata.content_type_id, mdata.object_id)
        if prev_key == key:
            if prev_value is not None and mdata.value*(1-error) < prev_value < mdata.value*(1+error):
                if third:
                    prev.delete()
                    delete_count += 1
                else:
                    third = True
            else:
                third = False
            prev_value = mdata.value
            prev_key = key
        else:
            prev_value = None
            prev_key = key
        prev = mdata
    return delete_count


def monthly_sum_old_values(dataset):
    aggregated = 0
    prev_key = None
    prev = None
    to_delete = []
    delete_count = 0
    for mdata in dataset.order_by('content_type_id', 'object_id', 'created_at'):
        key = (mdata.content_type_id, mdata.object_id, mdata.created_at.year, mdata.created_at.month)
        if prev_key is not None and prev_key != key:
            if prev.value != aggregated:
                prev.value = aggregated
                prev.save(update_fields=('value',))
            for obj in to_delete[:-1]:
                obj.delete()
                delete_count += 1
            aggregated = 0
            to_delete = []
        prev = mdata
        prev_key = key
        aggregated += mdata.value
        to_delete.append(mdata)
    return delete_count