"""SCP (Service Control Policy) data models."""

from dataclasses import dataclass, field
from typing import Any


@dataclass
class SCPStatement:
    """Represents a single statement in an SCP policy."""

    effect: str  # "Allow" or "Deny"
    actions: list[str] = field(default_factory=list)  # e.g., ["s3:*", "ec2:RunInstances"]
    resources: list[str] = field(default_factory=lambda: ["*"])
    not_actions: list[str] | None = None  # NotAction (inverse of actions)
    not_resources: list[str] | None = None  # NotResource (inverse of resources)
    principals: list[str] = field(default_factory=lambda: ["*"])  # Principal
    not_principals: list[str] | None = None  # NotPrincipal (inverse of principals)
    sid: str | None = None
    conditions: dict[str, Any] | None = None

    def __post_init__(self) -> None:
        """Validate the statement after initialization."""
        if self.effect not in ("Allow", "Deny"):
            raise ValueError(f"Invalid effect: {self.effect}. Must be 'Allow' or 'Deny'")

        # Ensure actions is a list
        if isinstance(self.actions, str):
            self.actions = [self.actions]

        # Ensure not_actions is a list if provided
        if isinstance(self.not_actions, str):
            self.not_actions = [self.not_actions]

        # Must have either actions or not_actions
        if not self.actions and not self.not_actions:
            raise ValueError("Statement must have either Action or NotAction")

        # Ensure resources is a list
        if isinstance(self.resources, str):
            self.resources = [self.resources]

        # Ensure not_resources is a list if provided
        if isinstance(self.not_resources, str):
            self.not_resources = [self.not_resources]

        # Ensure principals is a list
        if isinstance(self.principals, str):
            self.principals = [self.principals]

        # Ensure not_principals is a list if provided
        if isinstance(self.not_principals, str):
            self.not_principals = [self.not_principals]

    @property
    def uses_not_action(self) -> bool:
        """Check if this statement uses NotAction."""
        return self.not_actions is not None and len(self.not_actions) > 0

    @property
    def uses_not_resource(self) -> bool:
        """Check if this statement uses NotResource."""
        return self.not_resources is not None and len(self.not_resources) > 0

    @property
    def uses_not_principal(self) -> bool:
        """Check if this statement uses NotPrincipal."""
        return self.not_principals is not None and len(self.not_principals) > 0

    @property
    def effective_actions(self) -> list[str]:
        """Get the actions list (either Action or NotAction)."""
        if self.uses_not_action:
            return self.not_actions  # type: ignore
        return self.actions

    @property
    def effective_resources(self) -> list[str]:
        """Get the resources list (either Resource or NotResource)."""
        if self.uses_not_resource:
            return self.not_resources  # type: ignore
        return self.resources

    @property
    def effective_principals(self) -> list[str]:
        """Get the principals list (either Principal or NotPrincipal)."""
        if self.uses_not_principal:
            return self.not_principals  # type: ignore
        return self.principals


@dataclass
class SCPPolicy:
    """Represents a complete SCP policy document."""

    statements: list[SCPStatement]
    name: str | None = None
    version: str = "2012-10-17"

    def __post_init__(self) -> None:
        """Validate the policy after initialization."""
        if not self.statements:
            raise ValueError("Policy must have at least one statement")

        # Validate version format
        valid_versions = ("2012-10-17", "2008-10-17")
        if self.version not in valid_versions:
            raise ValueError(
                f"Invalid policy version: {self.version}. Must be one of {valid_versions}"
            )

    @property
    def policy_name(self) -> str | None:
        """Alias for name (backward compatibility)."""
        return self.name

    def get_deny_statements(self) -> list[SCPStatement]:
        """Get all Deny statements from this policy."""
        return [stmt for stmt in self.statements if stmt.effect == "Deny"]

    def get_allow_statements(self) -> list[SCPStatement]:
        """Get all Allow statements from this policy."""
        return [stmt for stmt in self.statements if stmt.effect == "Allow"]

    @property
    def has_deny_statements(self) -> bool:
        """Check if policy has any Deny statements."""
        return any(stmt.effect == "Deny" for stmt in self.statements)

    @property
    def has_allow_statements(self) -> bool:
        """Check if policy has any Allow statements."""
        return any(stmt.effect == "Allow" for stmt in self.statements)
