# *************************************************************************
#
#  Copyright (c) 2026 - Datatailr Inc.
#  All Rights Reserved.
#
#  This file is part of Datatailr and subject to the terms and conditions
#  defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
#  of this file, in parts or full, via any medium is strictly prohibited.
# *************************************************************************

from typing import Optional, Union

from datatailr.wrapper import dt__Group


DTUSERS_GROUP_NAME = "dtusers"

# Datatailr Group API Client
__client__ = dt__Group()


class Group:
    """
    Representing a Datatailr Group.

    This class provides methods to interact with the Datatailr Group API.
    It allows you to create, update, delete, and manage groups within the Datatailr platform.

    Attributes:
        name (str): The name of the group.
        members (list): A list of members in the group.
        group_id (int): The unique identifier for the group.
    """

    def __init__(self, name: str):
        """Create a Group instance by fetching group data from the platform.

        Args:
            name: The name of an existing group.

        Raises:
            ValueError: If a group with the given name does not exist.
        """
        self.__name: str = name
        self.__members: list = []
        self.__group_id: int | None = None

        self.__refresh__()

    def __repr__(self):
        return (
            f"Group(name={self.name}, members={self.members}, group_id={self.group_id})"
        )

    def __str__(self):
        return f"<Group: {self.name} | {self.group_id}>"

    def __eq__(self, other):
        if not isinstance(other, Group):
            return NotImplemented
        return (
            self.group_id == other.group_id
            and self.name == other.name
            and set(self.members) == set(other.members)
        )

    def __refresh__(self):
        if not self.name:
            raise ValueError("Name is not set. Cannot refresh group.")
        group = __client__.get(self.name)
        if group:
            self.__name = group["name"]
            self.__members = group["members"]
            self.__group_id = group["group_id"]
        else:
            raise ValueError(f"Group '{self.name}' does not exist.")

    @property
    def name(self) -> str:
        """The name of the group."""
        return self.__name

    @property
    def members(self) -> list:
        """List of user IDs that are members of this group."""
        return self.__members

    @property
    def group_id(self) -> int:
        """The unique numeric identifier of the group."""
        if self.__group_id is None:
            raise ValueError("Group ID is not set.")
        return self.__group_id

    @staticmethod
    def get(name_or_id: Union[str, int]) -> "Group":
        """Retrieve an existing group by name or numeric ID.

        Args:
            name_or_id: The group name (str) or group ID (int).

        Returns:
            The matching Group instance.

        Raises:
            ValueError: If no group with the given name or ID exists.
        """
        if isinstance(name_or_id, int):
            group = next((g for g in Group.ls() if g.group_id == name_or_id), None)
            if group is None:
                raise ValueError(f"Group with ID {name_or_id} not found.")
            return group
        return Group(name_or_id)

    @staticmethod
    def add(name: str) -> Optional["Group"]:
        """Create a new group with the specified name.

        Args:
            name: The name for the new group.

        Returns:
            The newly created Group, or ``None`` if the operation failed.
        """
        new_group = __client__.add(name, json_enrichened=True)
        return Group(new_group["name"]) if new_group else None

    @staticmethod
    def ls() -> list:
        """List all groups available in the Datatailr platform.

        Returns:
            A list of Group instances.
        """
        groups = __client__.ls()
        return [Group.from_dict(group_dict) for group_dict in groups]

    @staticmethod
    def remove(name: str) -> None:
        """Remove a group by its name.

        Args:
            name: The name of the group to remove.
        """
        __client__.rm(name)
        return None

    @staticmethod
    def exists(name: str) -> bool:
        """Check if a group exists by its name.

        Args:
            name: The group name to look up.

        Returns:
            ``True`` if the group exists, ``False`` otherwise.
        """
        return __client__.exists(name)

    @classmethod
    def from_dict(cls, data: dict) -> "Group":
        """Construct a Group instance from a dictionary.

        Args:
            data: A dictionary with keys ``"name"``, ``"members"``, and ``"group_id"``.

        Returns:
            A Group instance populated from the dictionary.

        Raises:
            ValueError: If required keys are missing from *data*.
        """
        obj = cls.__new__(cls)
        try:
            obj.__name = data["name"]
            obj.__members = data["members"]
            obj.__group_id = data["group_id"]
        except KeyError as e:
            raise ValueError(
                f"Can't construct group. Missing key in data dictionary: {e}. Got: {data}"
            )
        return obj

    def add_users(self, usernames: list) -> None:
        """Add users to the group.

        Args:
            usernames: A list of usernames to add.

        Raises:
            ValueError: If the group name is not set.
        """
        if not self.name:
            raise ValueError("Name is not set. Cannot add users.")
        __client__.add_users(self.name, ",".join(usernames))
        self.__refresh__()

    def remove_users(self, usernames: list) -> None:
        """Remove users from the group.

        Args:
            usernames: A list of usernames to remove.

        Raises:
            ValueError: If the group name is not set.
        """
        if not self.name:
            raise ValueError("Name is not set. Cannot remove users.")
        __client__.rm_users(self.name, ",".join(usernames))
        self.__refresh__()

    def is_member(self, user) -> bool:
        """Check whether a user belongs to this group.

        Args:
            user: A ``User`` instance or a user ID (int).

        Returns:
            ``True`` if the user is a member, ``False`` otherwise.

        Raises:
            ValueError: If the group name is not set.
        """
        if not self.name:
            raise ValueError("Name is not set. Cannot check membership.")
        return (
            user.user_id in self.members
            if hasattr(user, "user_id")
            else user in self.members
        )
