from datetime import timedelta

from django.db.models import Avg, Count, Max, Min, Sum
from django.db.models.functions import Trunc

from .base import BaseStorage

AGGREGATE_FUNCTIONS = {
    "avg": Avg,
    "min": Min,
    "max": Max,
    "sum": Sum,
    "count": Count,
}


class DjangoORMStorage(BaseStorage):
    """Default storage backend using Django ORM.

    Works with any database Django supports (PostgreSQL, SQLite, MySQL).
    For high-volume time-series, consider TimescaleDB or InfluxDB backends.
    """

    def store_reading(self, reading_model, device, timestamp, data):
        return reading_model.objects.create(
            device=device,
            timestamp=timestamp,
            **data,
        )

    def query_readings(
        self,
        reading_model,
        device=None,
        start=None,
        end=None,
        fields=None,
        limit=None,
        order="-timestamp",
    ):
        qs = reading_model.objects.all()

        if device is not None:
            qs = qs.filter(device=device)
        if start is not None:
            qs = qs.filter(timestamp__gte=start)
        if end is not None:
            qs = qs.filter(timestamp__lte=end)
        if fields:
            qs = qs.values("device_id", "timestamp", *fields)

        qs = qs.order_by(order)

        if limit:
            qs = qs[:limit]

        return qs

    def aggregate(
        self,
        reading_model,
        field,
        function="avg",
        device=None,
        start=None,
        end=None,
        interval=None,
    ):
        qs = reading_model.objects.all()

        if device is not None:
            qs = qs.filter(device=device)
        if start is not None:
            qs = qs.filter(timestamp__gte=start)
        if end is not None:
            qs = qs.filter(timestamp__lte=end)

        agg_func = AGGREGATE_FUNCTIONS.get(function)
        if agg_func is None:
            raise ValueError(f"Unknown aggregate function: {function}")

        # Simple aggregation (no time bucketing)
        if interval is None:
            return qs.aggregate(result=agg_func(field))

        # Time-bucketed aggregation
        trunc_kind = self._interval_to_trunc(interval)
        return (
            qs.annotate(bucket=Trunc("timestamp", trunc_kind))
            .values("bucket")
            .annotate(result=agg_func(field))
            .order_by("bucket")
        )

    def _interval_to_trunc(self, interval):
        """Convert an interval string or timedelta to a Django Trunc kind."""
        if isinstance(interval, str):
            mapping = {
                "minute": "minute",
                "hour": "hour",
                "day": "day",
                "week": "week",
                "month": "month",
            }
            return mapping.get(interval, "hour")

        if isinstance(interval, timedelta):
            seconds = interval.total_seconds()
            if seconds <= 60:
                return "minute"
            elif seconds <= 3600:
                return "hour"
            elif seconds <= 86400:
                return "day"
            elif seconds <= 604800:
                return "week"
            else:
                return "month"

        return "hour"
