# (c) cavaliba.com - tests / telemetry - raw -> minute / hour aggregation

from datetime import timedelta

from django.core.cache import cache as django_cache
from django.test import TestCase

import app_data.telemetry as T
import app_home.cache as cache
from app_data.models import DataTelemetryAggregate
from tests.helper import TELEMETRY_T0, add_telemetry_raw


def _agg(key, period, start=TELEMETRY_T0, id=""):
    return DataTelemetryAggregate.objects.get(key=key, period=period, start=start, distinct_id=id)


class TelemetryAggregateTest(TestCase):
    def setUp(self):
        cache.clear()
        T.reset_cache()

    def test_timer_minute_stats(self):
        for i in range(1, 101):
            add_telemetry_raw(TELEMETRY_T0 + timedelta(seconds=i * 0.5), "x.t", "timer", i)

        T.telemetry_aggregate(now=TELEMETRY_T0 + timedelta(minutes=3))

        agg = _agg("x.t", "minute")
        self.assertEqual((agg.count, agg.sum, agg.min, agg.max, agg.last), (100, 5050, 1, 100, 100))
        self.assertEqual((agg.mean, agg.median), (50.5, 50.5))
        self.assertEqual((agg.p90, agg.p95, agg.p99), (90, 95, 99))

    def test_counter_minute_and_hour(self):
        add_telemetry_raw(TELEMETRY_T0, "x.c", value=2)
        add_telemetry_raw(TELEMETRY_T0 + timedelta(minutes=1), "x.c", value=3)

        counts = T.telemetry_aggregate(now=TELEMETRY_T0 + timedelta(hours=1, minutes=2))

        self.assertEqual(counts["minute"], 2)
        self.assertEqual(_agg("x.c", "minute").sum, 2)
        hour = _agg("x.c", "hour")
        self.assertEqual((hour.count, hour.sum, hour.approx), (2, 5, False))

    def test_open_buckets_not_aggregated(self):
        add_telemetry_raw(TELEMETRY_T0 + timedelta(seconds=30), "x.c")

        T.telemetry_aggregate(now=TELEMETRY_T0 + timedelta(seconds=50))

        self.assertEqual(DataTelemetryAggregate.objects.count(), 0)

    def test_second_pass_writes_nothing(self):
        add_telemetry_raw(TELEMETRY_T0, "x.c")
        now = TELEMETRY_T0 + timedelta(minutes=3)
        T.telemetry_aggregate(now=now)

        counts = T.telemetry_aggregate(now=now)

        self.assertEqual(counts["minute"], 0)
        self.assertEqual(DataTelemetryAggregate.objects.filter(period="minute").count(), 1)

    def test_total_and_per_id_rows_with_fold(self):
        for i in range(25):
            add_telemetry_raw(TELEMETRY_T0, "x.c", id=f"id{i:02d}")
        add_telemetry_raw(TELEMETRY_T0, "x.c", id="id00")

        T.telemetry_aggregate(now=TELEMETRY_T0 + timedelta(minutes=3))

        self.assertEqual(_agg("x.c", "minute").count, 26)
        self.assertEqual(_agg("x.c", "minute", id="id00").count, 2)
        rows = DataTelemetryAggregate.objects.filter(period="minute").exclude(distinct_id="")
        self.assertEqual(rows.count(), T.DEFAULT_MAX_IDS + 1)
        self.assertEqual(_agg("x.c", "minute", id="_other").count, 25 - T.DEFAULT_MAX_IDS)

    def test_set_breakdown_and_histogram_buckets(self):
        for svalue in ["ok", "ok", "ko"]:
            add_telemetry_raw(TELEMETRY_T0, "x.s", "set", svalue=svalue)
        for value in [5, 50, 500, 50000]:
            add_telemetry_raw(TELEMETRY_T0, "x.h", "histogram", value)

        T.telemetry_aggregate(now=TELEMETRY_T0 + timedelta(minutes=3))

        agg = _agg("x.s", "minute")
        self.assertEqual((agg.data, agg.distinct), ({"ok": 2, "ko": 1}, 2))
        self.assertEqual(
            _agg("x.h", "minute").data,
            {"le_1": 0, "le_10": 1, "le_100": 1, "le_1000": 1, "le_10000": 0, "inf": 1},
        )

    def test_mixed_types_first_wins(self):
        add_telemetry_raw(TELEMETRY_T0, "x.m", "timer", 10)
        add_telemetry_raw(TELEMETRY_T0 + timedelta(seconds=1), "x.m", "counter", 1)

        T.telemetry_aggregate(now=TELEMETRY_T0 + timedelta(minutes=3))

        agg = _agg("x.m", "minute")
        self.assertEqual((agg.type, agg.count), ("timer", 1))

    def test_locked_pass_skipped(self):
        django_cache.add(T.AGGREGATE_LOCK, 1, 60)
        try:
            self.assertIsNone(T.telemetry_aggregate())
        finally:
            django_cache.delete(T.AGGREGATE_LOCK)
