"""CloudTrail event data models."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any


@dataclass
class CloudTrailEvent:
    """Represents a CloudTrail event from logs."""

    event_time: datetime
    event_name: str  # e.g., "RunInstances"
    event_source: str  # e.g., "ec2.amazonaws.com"
    user_identity: dict[str, Any]
    aws_region: str
    request_parameters: dict[str, Any] | None = None
    response_elements: dict[str, Any] | None = None
    error_code: str | None = None
    error_message: str | None = None
    resources: list[dict[str, Any]] | None = None
    source_ip_address: str | None = None
    user_agent: str | None = None
    event_id: str | None = None
    tls_details: dict[str, Any] | None = None
    vpc_endpoint_id: str | None = None
    additional_event_data: dict[str, Any] | None = None

    @classmethod
    def from_dict(cls, event_dict: dict[str, Any]) -> CloudTrailEvent:
        """Create a CloudTrailEvent from a raw CloudTrail JSON record.

        Args:
            event_dict: Dictionary representation of a CloudTrail event

        Returns:
            CloudTrailEvent object

        Raises:
            ValueError: If required fields are missing or unparseable
        """
        event_time_str = event_dict.get("eventTime")
        if not event_time_str:
            raise ValueError("Missing eventTime field")

        try:
            event_time = datetime.fromisoformat(event_time_str.replace("Z", "+00:00"))
        except ValueError:
            event_time = datetime.strptime(event_time_str, "%Y-%m-%dT%H:%M:%SZ").replace(
                tzinfo=timezone.utc
            )

        return cls(
            event_time=event_time,
            event_name=event_dict.get("eventName", ""),
            event_source=event_dict.get("eventSource", ""),
            user_identity=event_dict.get("userIdentity", {}),
            aws_region=event_dict.get("awsRegion", ""),
            request_parameters=event_dict.get("requestParameters"),
            response_elements=event_dict.get("responseElements"),
            error_code=event_dict.get("errorCode"),
            error_message=event_dict.get("errorMessage"),
            resources=event_dict.get("resources"),
            source_ip_address=event_dict.get("sourceIPAddress"),
            user_agent=event_dict.get("userAgent"),
            event_id=event_dict.get("eventID"),
            tls_details=event_dict.get("tlsDetails"),
            vpc_endpoint_id=event_dict.get("vpcEndpointId"),
            additional_event_data=event_dict.get("additionalEventData"),
        )

    @property
    def iam_action(self) -> str:
        """
        Convert CloudTrail event to IAM action format.

        Examples:
            ec2.amazonaws.com + RunInstances -> ec2:RunInstances
            s3.amazonaws.com + GetObject -> s3:GetObject
        """
        # Extract service name from event source
        # e.g., "ec2.amazonaws.com" -> "ec2"
        service = self.event_source.split(".")[0]
        return f"{service}:{self.event_name}"

    @property
    def principal_arn(self) -> str | None:
        """
        Extract principal ARN from userIdentity.

        Handles different identity types:
        - IAMUser: arn:aws:iam::123456789012:user/username
        - AssumedRole: arn:aws:sts::123456789012:assumed-role/role-name/session
        - Root: arn:aws:iam::123456789012:root
        """
        if not self.user_identity:
            return None

        # For assumed roles
        if self.user_identity.get("type") == "AssumedRole":
            return self.user_identity.get("arn")

        # For IAM users
        if self.user_identity.get("type") == "IAMUser":
            return self.user_identity.get("arn")

        # For root user
        if self.user_identity.get("type") == "Root":
            account_id = self.user_identity.get("accountId")
            return f"arn:aws:iam::{account_id}:root" if account_id else None

        # For federated users
        if self.user_identity.get("type") == "FederatedUser":
            return self.user_identity.get("arn")

        # For AWS service
        if self.user_identity.get("type") == "AWSService":
            return self.user_identity.get("invokedBy")

        return self.user_identity.get("arn")

    @property
    def principal_type(self) -> str:
        """Get the type of principal (IAMUser, AssumedRole, Root, etc.)."""
        return self.user_identity.get("type", "Unknown") if self.user_identity else "Unknown"

    @property
    def account_id(self) -> str | None:
        """Extract AWS account ID from userIdentity."""
        if not self.user_identity:
            return None
        return self.user_identity.get("accountId")

    @property
    def is_service_linked_role(self) -> bool:
        """Check if this event was performed by a service-linked role.

        SCPs do not apply to service-linked roles. Detected by:
        1. Principal ARN contains '/aws-service-role/'
        2. userIdentity.type is 'AWSService' (service-initiated events)
        """
        arn = self.principal_arn
        if arn and "/aws-service-role/" in arn:
            return True
        if self.user_identity and self.user_identity.get("type") == "AWSService":
            return True
        return False

    @property
    def was_denied(self) -> bool:
        """Check if this event was denied (had an error)."""
        return self.error_code is not None

    def __str__(self) -> str:
        """String representation of the event."""
        return (
            f"CloudTrailEvent("
            f"action={self.iam_action}, "
            f"principal={self.principal_arn}, "
            f"time={self.event_time.isoformat()}"
            f")"
        )
