"""
SecureAgent: Agent wrapper with trust zone enforcement and encrypted memories.

Implements SEC-01 (encryption), SEC-02 (trust zones), SEC-03 (access grants),
SEC-04 (access validation with logging).

This module provides the final integration layer that combines all security
components into a cohesive agent wrapper.
"""

from __future__ import annotations

import logging
import uuid
from datetime import datetime, timezone
from typing import Optional, List, Set

from pydantic import BaseModel, Field

from .trust_zones import TrustZone, ZoneAssignment
from .access_control import ZoneAccessManager
from .encryption import MemoryEncryptor
from gsd_rlm.memory.hmem import Episode, EpisodeType
from gsd_rlm.agents.definition import AgentDefinition


class SecureAgentConfig(BaseModel):
    """
    Configuration for SecureAgent (SEC-01, SEC-02).

    Defines the security settings for an agent including trust zone
    assignment and encryption preferences.

    Attributes:
        agent_id: Unique identifier for the agent.
        zone: Trust zone classification (default: INTERNAL).
        encryption_enabled: Whether to enable memory encryption.
        key_path: Optional path to encryption key file.
    """

    model_config = {"extra": "forbid"}

    agent_id: str = Field(..., min_length=1, description="Unique agent identifier")
    zone: TrustZone = Field(
        default=TrustZone.INTERNAL,
        description="Trust zone classification",
    )
    encryption_enabled: bool = Field(
        default=True,
        description="Whether to enable memory encryption",
    )
    key_path: Optional[str] = Field(
        default=None,
        description="Optional path to encryption key file",
    )


class SecureAgent:
    """
    Agent wrapper with trust zone enforcement and encrypted memories.

    Combines:
    - Memory encryption (SEC-01)
    - Trust zone assignment (SEC-02)
    - Access grant management (SEC-03)
    - Access validation with logging (SEC-04)

    This class wraps an AgentDefinition with security features, providing
    a unified interface for secure agent operations.

    Example:
        >>> from gsd_rlm.security import SecureAgent, SecureAgentConfig, ZoneAccessManager, TrustZone
        >>> from gsd_rlm.agents.definition import AgentDefinition
        >>>
        >>> definition = AgentDefinition(
        ...     name="Secure Researcher",
        ...     role="Research agent with confidential access",
        ...     goal="Perform secure research",
        ...     backstory="Trusted agent for sensitive operations",
        ... )
        >>> manager = ZoneAccessManager()
        >>> config = SecureAgentConfig(
        ...     agent_id="secure-researcher",
        ...     zone=TrustZone.CONFIDENTIAL,
        ... )
        >>> agent = SecureAgent(definition, config, manager)
        >>> agent.zone
        <TrustZone.CONFIDENTIAL: 'confidential'>
    """

    def __init__(
        self,
        definition: AgentDefinition,
        config: SecureAgentConfig,
        access_manager: ZoneAccessManager,
    ):
        """
        Initialize SecureAgent with definition, config, and access manager.

        Args:
            definition: Agent definition with role, goal, backstory.
            config: Security configuration with zone and encryption settings.
            access_manager: ZoneAccessManager for cross-zone access validation.
        """
        self.definition = definition
        self.config = config
        self.agent_id = config.agent_id
        self._access_manager = access_manager
        self._logger = logging.getLogger("gsd_rlm.security")

        # Initialize encryption if enabled
        self._encryptor: Optional[MemoryEncryptor] = None
        if config.encryption_enabled:
            if config.key_path:
                with open(config.key_path, "rb") as f:
                    key = f.read()
                self._encryptor = MemoryEncryptor(key=key)
            else:
                self._encryptor = MemoryEncryptor()

        # Register with access manager
        self._access_manager.assign_agent_to_zone(self.agent_id, config.zone)

    @property
    def zone(self) -> TrustZone:
        """
        Get this agent's trust zone.

        Returns:
            The TrustZone this agent is assigned to.
        """
        return self.config.zone

    @property
    def encryptor(self) -> Optional[MemoryEncryptor]:
        """
        Get the encryptor for this agent.

        Returns:
            MemoryEncryptor if encryption is enabled, None otherwise.
        """
        return self._encryptor

    def can_access(self, target_agent_id: str, operation: str = "read") -> bool:
        """
        Check if this agent can access another agent's data (SEC-04).

        Delegates to ZoneAccessManager.validate_access which checks:
        1. Same agent always has access
        2. Hierarchical zone access (higher zones can access lower)
        3. Explicit grants (not expired, operation allowed)

        Args:
            target_agent_id: Agent whose data is being accessed.
            operation: Operation being attempted (default: "read").

        Returns:
            True if access is allowed, False if denied.
        """
        result = self._access_manager.validate_access(
            source_agent_id=self.agent_id,
            target_agent_id=target_agent_id,
            operation=operation,
        )

        if not result:
            self._log_violation(target_agent_id, operation)

        return result

    def grant_access_to(
        self,
        target_agent_id: str,
        granted_by: str,
        allowed_operations: Optional[Set[str]] = None,
        expires_at: Optional[datetime] = None,
    ):
        """
        Grant this agent access to another agent's data (SEC-03).

        Creates an explicit permission for this agent to access the target
        agent, overriding default hierarchical access control.

        Args:
            target_agent_id: Agent to grant access to.
            granted_by: User ID who granted access.
            allowed_operations: Set of allowed operations (default: {"read"}).
            expires_at: Optional expiration timestamp.

        Returns:
            The created AccessGrant.
        """
        return self._access_manager.grant_access(
            source_agent_id=self.agent_id,
            target_agent_id=target_agent_id,
            granted_by=granted_by,
            allowed_operations=allowed_operations,
            expires_at=expires_at,
        )

    def create_episode(
        self,
        episode_type: EpisodeType,
        context: str,
        action: str,
        outcome: str,
        success: bool = False,
        session_id: str = "",
    ) -> Episode:
        """
        Create an episode with zone tag and optional encryption (SEC-01, SEC-02).

        Creates an Episode tagged with the agent's trust zone. The episode
        can then be stored via EncryptedEpisodeStore for transparent encryption.

        Args:
            episode_type: Type of episode (TASK_EXECUTION, etc.).
            context: What led to the action.
            action: What was done.
            outcome: What happened.
            success: Whether the action was successful.
            session_id: Session identifier.

        Returns:
            Episode with zone tag, ready for encrypted storage.
        """
        episode = Episode(
            episode_id=f"ep-{self.agent_id}-{uuid.uuid4().hex[:12]}",
            agent_id=self.agent_id,
            session_id=session_id,
            episode_type=episode_type,
            context=context,
            action=action,
            outcome=outcome,
            success=success,
            tags=[f"zone:{self.config.zone.value}"],
        )

        return episode

    def _log_violation(self, target_agent_id: str, operation: str) -> None:
        """
        Log trust zone violation for audit (SEC-04).

        Creates a warning log entry when access is denied. The ZoneAccessManager
        also creates a ViolationLog entry for the audit trail.

        Args:
            target_agent_id: Agent that was accessed.
            operation: Operation that was attempted.
        """
        self._logger.warning(
            f"Trust zone violation: agent={self.agent_id} "
            f"attempted {operation} on target={target_agent_id} "
            f"zone={self.config.zone.value}"
        )

    def get_zone_assignment(self) -> ZoneAssignment:
        """
        Get the zone assignment record for this agent.

        Returns:
            ZoneAssignment with agent_id, zone, and assignment metadata.
        """
        return ZoneAssignment(
            agent_id=self.agent_id,
            zone=self.config.zone,
            assigned_by="system",
            reason=f"Assigned during SecureAgent initialization",
        )
