"""Set accessor for set operations."""
from __future__ import annotations

from typing import NoReturn, TYPE_CHECKING

from cqla.exprs import base as exprs_base
from cqla.accessors.base import Accessor

if TYPE_CHECKING:
    from cqla.exprs.base import Expr


def _cast_to_set(whatever: object) -> set | NoReturn:
    try:
        return set(whatever)
    except TypeError:
        raise TypeError(f'Cannot cast object of type "{type(whatever)}" to set')


class Set(Accessor):
    """Accessor for set operations."""

    def _expr_repr(self) -> str:
        return f"{self._expr!r}.set"

    def _get_set(self, record: object) -> set:
        return _cast_to_set(self._expr(record))

    def contains(self, item: object) -> Expr:
        return exprs_base.Expr(
            lambda r: item in self._get_set(r),
            f"{self._expr_repr()}.contains({item!r})"
        )

    def is_subset(self, other: set) -> Expr:
        return exprs_base.Expr(
            lambda r: self._get_set(r).issubset(other),
            f"{self._expr_repr()}.is_subset({other!r})"
        )

    def is_superset(self, other: set) -> Expr:
        return exprs_base.Expr(
            lambda r: self._get_set(r).issuperset(other),
            f"{self._expr_repr()}.is_superset({other!r})"
        )

    def is_disjoint(self, other: set) -> Expr:
        return exprs_base.Expr(
            lambda r: self._get_set(r).isdisjoint(other),
            f"{self._expr_repr()}.is_disjoint({other!r})"
        )

    def intersects(self, other: set) -> Expr:
        return exprs_base.Expr(
            lambda r: not self._get_set(r).isdisjoint(other),
            f"{self._expr_repr()}.intersects({other!r})"
        )

    def len(self) -> Expr:
        return exprs_base.Expr(
            lambda r: len(self._get_set(r)),
            f"{self._expr_repr()}.len()"
        )

    def is_empty(self) -> Expr:
        return exprs_base.Expr(
            lambda r: len(self._get_set(r)) == 0,
            f"{self._expr_repr()}.is_empty()"
        )

    def union(self, other: set) -> Expr:
        return exprs_base.Expr(
            lambda r: self._get_set(r).union(other),
            f"{self._expr_repr()}.union({other!r})"
        )

    def intersection(self, other: set) -> Expr:
        return exprs_base.Expr(
            lambda r: self._get_set(r).intersection(other),
            f"{self._expr_repr()}.intersection({other!r})"
        )

    def difference(self, other: set) -> Expr:
        return exprs_base.Expr(
            lambda r: self._get_set(r).difference(other),
            f"{self._expr_repr()}.difference({other!r})"
        )
