from __future__ import annotations

import enum
from typing import Literal

RESOURCE_ARN_PATTERN = (
    r"^arn:(?P<partition>[^:\n]*):(?P<service>[^:\n]*):(?P<region>[^:\n]*):"
    r"(?P<account_id>[^:\n]*):(?P<ignore>(?P<resource_type>[^:\/\n]*)[:\/])?(?P<resource_id>.*)$"
)

AttachmentTargetType = Literal[
    "AWSAccount",
    "IAMGroup",
    "AssumedRole",
    "IAMUser",
    "Organizational Unit or Organization Root",
    "Resource: ECR Repository",
    "Resource: IAM Role",
    "Resource: KMS Key",
    "Resource: Lambda Function",
    "Resource: S3 Bucket",
    "Resource: SecretsManager Secret",
]


class AccessDeniedReason(str, enum.Enum):
    ALLOWED = "Allowed"
    CROSS_ACCOUNT_MISSING_ALLOW = (
        "Missing allows in an identity-based and "
        "a resource-based policy in cross-account access"
    )
    IDENTITY_POLICY_EXPLICIT_DENY = "Explicit deny in an identity-based policy"
    IDENTITY_POLICY_MISSING_ALLOW = "Missing allow in an identity-based policy"
    PERMISSIONS_BOUNDARY_EXPLICIT_DENY = "Explicit deny in a permissions boundary policy"
    PERMISSIONS_BOUNDARY_MISSING_ALLOW = "Missing allow in a permissions boundary policy"
    RESOURCE_POLICY_EXPLICIT_DENY = "Explicit deny in a resource-based policy"
    RESOURCE_POLICY_MISSING_ALLOW = (
        "Missing allow in a resource-based policy in cross-account access"
    )
    SCP_EXPLICIT_DENY = "Explicit deny in a service-control policy"
    SCP_MISSING_ALLOW = "Missing allow in a service-control policy"
    INVALID_ACTION = "Invalid action"
    ERROR = "Error"


class PolicyType(str, enum.Enum):
    IDENTITY_INLINE_POLICY = "Inline IAM Policy"
    IDENTITY_MANAGED_POLICY = "Managed IAM Policy"
    RESOURCE_POLICY = "Resource Policy"
    PERMISSIONS_BOUNDARY_POLICY = "Permissions Boundary Policy"
    SERVICE_CONTROL_POLICY = "Service Control Policy"
    COMBINED_SERVICE_CONTROL_POLICY = "Merged Composite Service Control Policy"


class AccessUndeniedError(Exception):
    def __init__(self, message: str, access_denied_reason: AccessDeniedReason):
        super(AccessUndeniedError, self).__init__(message)
        self.message = message
        self.access_denied_reason = access_denied_reason


class Config(object):
    def __init__(self) -> None:
        self.cross_account_role_name = None
        self.management_account_role_arn = None
        self.management_account_id = ""
        self.organization_nodes = dict()
        self.output_file = None
        self.output_json = {"Results": []}
        self.session = None
        self.suppress_output = False
        self.account_id = None
        self.iam_client = None


class Policy(object):
    def __init__(
        self,
        attachment_target_arn: str,
        attachment_target_type: AttachmentTargetType,
        policy_name: str,
        policy_arn: str,
        policy_document: str,
        policy_type: PolicyType,
    ) -> None:
        self.attachment_target_arn = attachment_target_arn
        self.attachment_target_type = attachment_target_type
        self.policy_name = policy_name
        self.policy_arn = policy_arn
        self.policy_document = policy_document
        self.policy_type = policy_type


class MatchedPolicy(Policy):
    def __init__(self, matched_statement: str, policy: Policy):
        super().__init__(
            policy.attachment_target_arn,
            policy.attachment_target_type,
            policy.policy_name,
            policy.policy_arn,
            policy.policy_document,
            policy.policy_type,
        )
        self.matched_statement = matched_statement


class Principal(object):
    def __init__(self) -> None:
        self.session_name = ""
        self.arn = ""
        self.type = ""
        self.account_id = ""
        self.name = ""


class Resource(object):
    def __init__(self, arn: str, account_id: str) -> None:
        self.arn = arn
        self.account_id = account_id
