"""Accessor and aggregation mixins."""
from __future__ import annotations

import statistics

from cqla import accessors
from cqla.exprs.base import AggExpr, ContextExpr


class AccessorMixin:
    """Mixin that provides accessor properties for expressions and fields."""

    @property
    def str(self) -> accessors.String:
        return accessors.String(self)

    @property
    def list(self) -> accessors.List:
        return accessors.List(self)

    @property
    def dt(self) -> accessors.Datetime:
        return accessors.Datetime(self)

    @property
    def set(self) -> accessors.Set:
        return accessors.Set(self)

    @property
    def struct(self) -> accessors.Struct:
        return accessors.Struct(self)


class AggMixin:
    """Mixin that provides aggregation methods for expressions and fields."""

    def mean(self) -> AggExpr:
        return AggExpr(self, statistics.mean, f"{self!r}.mean()")

    def sum(self) -> AggExpr:
        return AggExpr(self, sum, f"{self!r}.sum()")

    def min(self) -> AggExpr:
        return AggExpr(self, min, f"{self!r}.min()")

    def max(self) -> AggExpr:
        return AggExpr(self, max, f"{self!r}.max()")

    def count(self) -> AggExpr:
        return AggExpr(self, len, f"{self!r}.count()")

    def first(self) -> AggExpr:
        return AggExpr(self, lambda vals: vals[0], f"{self!r}.first()")

    def last(self) -> AggExpr:
        return AggExpr(self, lambda vals: vals[-1], f"{self!r}.last()")

    def n_unique(self) -> AggExpr:
        return AggExpr(self, lambda vals: len(set(vals)), f"{self!r}.n_unique()")

    def median(self) -> AggExpr:
        return AggExpr(self, statistics.median, f"{self!r}.median()")

    def all(self) -> AggExpr:
        """True if all values are truthy."""
        return AggExpr(self, lambda vals: all(vals), f"{self!r}.all()")

    def any(self) -> AggExpr:
        """True if any value is truthy."""
        return AggExpr(self, lambda vals: any(vals), f"{self!r}.any()")

    def has_nulls(self) -> AggExpr:
        """True if any value is None."""
        return AggExpr(self, lambda vals: any(v is None for v in vals), f"{self!r}.has_nulls()")

    # Context-dependent predicates
    def is_duplicated(self) -> ContextExpr:
        """True for rows where this value appears more than once."""
        return ContextExpr(self, "is_duplicated", f"{self!r}.is_duplicated()")

    def is_unique(self) -> ContextExpr:
        """True for rows where this value appears exactly once."""
        return ContextExpr(self, "is_unique", f"{self!r}.is_unique()")

    def is_first_distinct(self) -> ContextExpr:
        """True for the first occurrence of each distinct value."""
        return ContextExpr(self, "is_first_distinct", f"{self!r}.is_first_distinct()")

    def is_last_distinct(self) -> ContextExpr:
        """True for the last occurrence of each distinct value."""
        return ContextExpr(self, "is_last_distinct", f"{self!r}.is_last_distinct()")
