# *************************************************************************
#
#  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 __future__ import annotations

import json
from enum import Enum

from datatailr import Group, User
from datatailr.group import DTUSERS_GROUP_NAME


class Permission(Enum):
    """Enumeration of permission types used in Access Control Lists.

    Each permission controls a different aspect of access to a Datatailr resource.

    Attributes:
        READ: Allows reading or viewing the resource.
        WRITE: Allows modifying or updating the resource.
        OPERATE: Allows executing or operating the resource (e.g., running a job).
        ACCESS: Allows accessing the resource (e.g., opening an app).
        PROMOTE: Allows promoting the resource to the next environment.
    """

    READ = "read"
    WRITE = "write"
    OPERATE = "operate"
    ACCESS = "access"
    PROMOTE = "promote"

    def __str__(self):
        return self.value


class ACL:
    """
    A class to represent an Access Control List (ACL) for managing permissions.
    """

    def __init__(
        self,
        permissions: dict[Permission, list[User | Group]] | None = None,
    ):
        """Initialize an ACL instance.

        Args:
            permissions: A mapping of permission types to lists of users and/or groups.

        Example:
            ```python
            from datatailr import ACL, Permission, User, Group
            acl = ACL({
                Permission.READ: [User.get("alice"), Group.get("analysts")],
                Permission.WRITE: [User.get("alice")],
            })
            ```
        """
        if not permissions:
            user: User = User.signed_user()
            group: Group = Group.get(DTUSERS_GROUP_NAME)
            if user is None or group is None:
                raise ValueError("Signed user or primary group not found.")
            _permissions: dict[Permission, list[User | Group]] = {
                Permission.READ: [user, group],
                Permission.WRITE: [user],
                Permission.OPERATE: [user],
                Permission.ACCESS: [user, group],
                Permission.PROMOTE: [user],
            }
        else:
            _permissions = permissions
        self.permissions = _permissions

    def __repr__(self):
        return "datatailr.ACL:\n" + "\n".join(
            f"{str(permission_type):>10}: {[str(entity) for entity in entities]}"
            for permission_type, entities in self.permissions.items()
        )

    def to_dict(self) -> dict[str, list[int]]:
        """Convert the ACL to a dictionary mapping permission names to entity IDs.

        Group IDs are stored as negative numbers to distinguish them from user IDs.

        Returns:
            A dictionary where keys are permission names (e.g. ``"read"``) and
            values are lists of integer entity IDs.
        """
        acl_dict: dict[str, list[int]] = {}
        for permission_type, entities in self.permissions.items():
            ids: list[int] = []
            for entity in entities:
                if isinstance(entity, Group):
                    ids.append(-entity.group_id)
                else:
                    uid = entity.user_id
                    if uid is not None:
                        ids.append(uid)
            acl_dict[str(permission_type)] = ids
        return acl_dict

    @classmethod
    def from_dict(cls, acl_dict: dict) -> ACL:
        """
        Create an ACL instance from a dictionary.
        """
        permissions: dict[Permission, list[User | Group]] = {}

        for permission_type_str, entity_ids in acl_dict.items():
            permission_type = Permission(permission_type_str)
            entities: list[User | Group] = []

            for entity_id in entity_ids:
                entity = (
                    Group.get(abs(entity_id)) if entity_id < 0 else User.get(entity_id)
                )
                if entity is None:
                    raise ValueError(
                        f"{'Group' if entity_id < 0 else 'User'} id {abs(entity_id)} not found"
                    )
                entities.append(entity)

            permissions[permission_type] = entities

        return ACL(permissions=permissions)

    def to_cli_command(self) -> dict:
        """
        Convert the ACL to a command string of the form: group1:rw,group2:rw,....
        """
        user_acls: list[str] = []
        group_acls: list[str] = []
        for permission_type, entities in self.permissions.items():
            perm_char: str = permission_type.value[0]
            for entity in entities:
                if isinstance(entity, User):
                    user_acls.append(f"{entity.name}:{perm_char}")
                elif isinstance(entity, Group):
                    group_acls.append(f"{entity.name}:{perm_char}")

        return {"users": ",".join(user_acls), "groups": ",".join(group_acls)}

    def to_json(self) -> str:
        """Serialize the ACL to a JSON string.

        Returns:
            A JSON string representation of the ACL.
        """
        return json.dumps(self.to_dict())
