"""
Highflame Policy Engine - Python Wrapper
Wraps cedarpy with Highflame-specific types
"""

from dataclasses import dataclass
from typing import Any, Optional

import cedarpy

from .entities import EntityType
from .actions import ActionType


@dataclass
class EntityUID:
    """Cedar entity unique identifier."""
    type: str
    id: str


@dataclass
class Decision:
    """Result of a policy evaluation."""
    effect: str  # "Allow" or "Deny"
    determining_policies: list[str]
    reason: Optional[str] = None

    def is_allowed(self) -> bool:
        return self.effect == "Allow"

    def is_denied(self) -> bool:
        return self.effect == "Deny"


class PolicyEngine:
    """
    PolicyEngine wraps cedarpy with Highflame schema types.

    Example:
        engine = PolicyEngine()
        engine.load_policies_from_file("policies/palisade/policy.cedar")

        decision = engine.evaluate(
            principal_type=EntityType.SCANNER,
            principal_id="palisade",
            action=ActionType.SCAN_ARTIFACT,
            resource_type=EntityType.ARTIFACT,
            resource_id="/path/to/model.safetensors",
            context={
                "artifact_format": "safetensors",
                "severity": "HIGH",
                "environment": "production",
            }
        )

        if decision.is_denied():
            print(f"Blocked by: {decision.determining_policies}")
    """

    def __init__(self):
        self._policies: str = ""
        self._schema: Optional[str] = None

    def load_policies_from_file(self, path: str) -> None:
        """Load Cedar policies from a file."""
        with open(path, "r") as f:
            self._policies = f.read()

    def load_policies(self, policies: str) -> None:
        """Load Cedar policies from a string."""
        self._policies = policies

    def load_schema_from_file(self, path: str) -> None:
        """Load Cedar schema from a file."""
        with open(path, "r") as f:
            self._schema = f.read()

    def load_schema(self, schema: str) -> None:
        """Load Cedar schema from a string."""
        self._schema = schema

    def evaluate(
        self,
        principal_type: str,
        principal_id: str,
        action: str,
        resource_type: str,
        resource_id: str,
        context: Optional[dict[str, Any]] = None,
    ) -> Decision:
        """
        Evaluate a policy request and return a decision.

        Args:
            principal_type: Entity type of the principal (e.g., EntityType.USER)
            principal_id: ID of the principal
            action: Action being performed (e.g., ActionType.SCAN_ARTIFACT)
            resource_type: Entity type of the resource (e.g., EntityType.ARTIFACT)
            resource_id: ID of the resource
            context: Optional context attributes for the request

        Returns:
            Decision with effect, determining policies, and optional reason
        """
        # Format entity UIDs for Cedar
        principal = f'{principal_type}::"{principal_id}"'
        action_uid = f'Action::"{action}"'
        resource = f'{resource_type}::"{resource_id}"'

        # Call cedarpy (v4.8.0+ uses request dict instead of separate args)
        request = {
            "principal": principal,
            "action": action_uid,
            "resource": resource,
            "context": context or {},
        }
        result = cedarpy.is_authorized(
            request=request,
            policies=self._policies,
            entities=[],
        )

        # Map result to Decision (using result.allowed for cleaner API)
        return Decision(
            effect="Allow" if result.allowed else "Deny",
            determining_policies=list(result.diagnostics.reasons) if result.diagnostics else [],
            reason="; ".join(result.diagnostics.errors) if result.diagnostics and result.diagnostics.errors else None,
        )

    def validate_policies(self, policies: str) -> list[str]:
        """
        Validate policies against the loaded schema.

        Returns:
            List of validation error messages, or empty list if valid.
        """
        if not self._schema:
            return ["No schema loaded for validation"]

        result = cedarpy.validate(
            policies=policies,
            schema=self._schema,
        )

        if result.validation_errors:
            return [str(e) for e in result.validation_errors]
        return []
