# Path: repokid/datasource/plugin.py
# class DatasourcePlugin(RepokidPlugin, Generic[KT, VT]):
#     """A dict-like container that can be used to retrieve and store data"""
# 
#     _data: Dict[KT, VT]
#     _seeded: List[str]
# 
#     def __init__(self, config: Optional[RepokidConfig] = None):
#         super().__init__(config=config)
#         self._data = {}
#         self._seeded = []
# 
#     def __getitem__(self, name: KT) -> VT:
#         return self._data[name]
# 
#     def __iter__(self) -> Iterator[VT]:
#         return iter(cast(Iterable[VT], self._data))
# 
#     def keys(self) -> Iterable[KT]:
#         return self._data.keys()
# 
#     def items(self) -> ItemsView[KT, VT]:
#         return self._data.items()
# 
#     def values(self) -> ValuesView[VT]:
#         return self._data.values()
# 
#     def get(self, identifier: KT) -> VT:
#         raise NotImplementedError
# 
#     def seed(self, identifier: KT) -> Iterable[KT]:
#         raise NotImplementedError
# 
#     def reset(self) -> None:
#         logger.debug("resetting %s", type(self).__name__)
#         self._data = {}
#         self._seeded = []
# 
# Path: repokid/exceptions.py
# class NotFoundError(Exception):
#     pass
# 
# Path: repokid/plugin.py
# class Singleton(M_A):
#     _instances: Dict[str, Singleton] = {}
# 
#     def __call__(cls, *args: Any, **kwargs: Any) -> Singleton:
#         if cls.__name__ not in cls._instances:
#             cls._instances[cls.__name__] = super(Singleton, cls).__call__(
#                 *args, **kwargs
#             )
#         return cls._instances[cls.__name__]
# 
# Path: repokid/types.py
# KT = TypeVar("KT")
# VT = TypeVar("VT")
# 
# Path: repokid/types.py
# KT = TypeVar("KT")
# VT = TypeVar("VT")


import copy
import logging
from typing import Dict
from typing import Iterable
from typing import Optional
from cloudaux.aws.iam import get_account_authorization_details
from cloudaux.orchestration.aws.iam.role import FLAGS
from cloudaux.orchestration.aws.iam.role import get_role
from repokid.datasource.plugin import DatasourcePlugin
from repokid.exceptions import NotFoundError
from repokid.plugin import Singleton
from repokid.types import IAMEntry
from repokid.types import RepokidConfig

logger = logging.getLogger("repokid")


class IAMDatasource(DatasourcePlugin[str, IAMEntry], metaclass=Singleton):
    _arn_to_id: Dict[str, str] = {}

    def __init__(self, config: Optional[RepokidConfig] = None):
        super().__init__(config=config)

    def _fetch_account(self, account_number: str) -> Dict[str, IAMEntry]:
        logger.info("getting role data for account %s", account_number)
        conn = copy.deepcopy(self.config.get("connection_iam", {}))
        conn["account_number"] = account_number
        auth_details = get_account_authorization_details(filter="Role", **conn)
        auth_details_by_id = {item["Arn"]: item for item in auth_details}
        self._arn_to_id.update({item["Arn"]: item["RoleId"] for item in auth_details})
        # convert policies list to dictionary to maintain consistency with old call which returned a dict
        for _, data in auth_details_by_id.items():
            data["RolePolicyList"] = {
                item["PolicyName"]: item["PolicyDocument"]
                for item in data["RolePolicyList"]
            }
        return auth_details_by_id

    def _fetch(self, arn: str) -> IAMEntry:
        logger.info("getting role data for role %s", arn)
        conn = copy.deepcopy(self.config["connection_iam"])
        conn["account_number"] = arn.split(":")[4]
        role = {"RoleName": arn.split("/")[-1]}

# Path: repokid/datasource/plugin.py
# class DatasourcePlugin(RepokidPlugin, Generic[KT, VT]):
#     """A dict-like container that can be used to retrieve and store data"""
# 
#     _data: Dict[KT, VT]
#     _seeded: List[str]
# 
#     def __init__(self, config: Optional[RepokidConfig] = None):
#         super().__init__(config=config)
#         self._data = {}
#         self._seeded = []
# 
#     def __getitem__(self, name: KT) -> VT:
#         return self._data[name]
# 
#     def __iter__(self) -> Iterator[VT]:
#         return iter(cast(Iterable[VT], self._data))
# 
#     def keys(self) -> Iterable[KT]:
#         return self._data.keys()
# 
#     def items(self) -> ItemsView[KT, VT]:
#         return self._data.items()
# 
#     def values(self) -> ValuesView[VT]:
#         return self._data.values()
# 
#     def get(self, identifier: KT) -> VT:
#         raise NotImplementedError
# 
#     def seed(self, identifier: KT) -> Iterable[KT]:
#         raise NotImplementedError
# 
#     def reset(self) -> None:
#         logger.debug("resetting %s", type(self).__name__)
#         self._data = {}
#         self._seeded = []
# 
# Path: repokid/exceptions.py
# class NotFoundError(Exception):
#     pass
# 
# Path: repokid/plugin.py
# class Singleton(M_A):
#     _instances: Dict[str, Singleton] = {}
# 
#     def __call__(cls, *args: Any, **kwargs: Any) -> Singleton:
#         if cls.__name__ not in cls._instances:
#             cls._instances[cls.__name__] = super(Singleton, cls).__call__(
#                 *args, **kwargs
#             )
#         return cls._instances[cls.__name__]
# 
# Path: repokid/types.py
# KT = TypeVar("KT")
# VT = TypeVar("VT")
# 
# Path: repokid/types.py
# KT = TypeVar("KT")
# VT = TypeVar("VT")


# Path: repokid/datasource/iam.py
import copy
import logging
from typing import Dict
from typing import Iterable
from typing import Optional
from cloudaux.aws.iam import get_account_authorization_details
from cloudaux.orchestration.aws.iam.role import FLAGS
from cloudaux.orchestration.aws.iam.role import get_role
from repokid.datasource.plugin import DatasourcePlugin
from repokid.exceptions import NotFoundError
from repokid.plugin import Singleton
from repokid.types import IAMEntry
from repokid.types import RepokidConfig


logger = logging.getLogger("repokid")


class IAMDatasource(DatasourcePlugin[str, IAMEntry], metaclass=Singleton):
    _arn_to_id: Dict[str, str] = {}

    def __init__(self, config: Optional[RepokidConfig] = None):
        super().__init__(config=config)

    def _fetch_account(self, account_number: str) -> Dict[str, IAMEntry]:
        logger.info("getting role data for account %s", account_number)
        conn = copy.deepcopy(self.config.get("connection_iam", {}))
        conn["account_number"] = account_number
        auth_details = get_account_authorization_details(filter="Role", **conn)
        auth_details_by_id = {item["Arn"]: item for item in auth_details}
        self._arn_to_id.update({item["Arn"]: item["RoleId"] for item in auth_details})
        # convert policies list to dictionary to maintain consistency with old call which returned a dict
        for _, data in auth_details_by_id.items():
            data["RolePolicyList"] = {
                item["PolicyName"]: item["PolicyDocument"]
                for item in data["RolePolicyList"]
            }
        return auth_details_by_id

    def _fetch(self, arn: str) -> IAMEntry:
        logger.info("getting role data for role %s", arn)
        conn = copy.deepcopy(self.config["connection_iam"])
        conn["account_number"] = arn.split(":")[4]
        role = {"RoleName": arn.split("/")[-1]}

        role_info: IAMEntry = get_role(role, flags=FLAGS.INLINE_POLICIES, **conn)