from pathlib import Path
from typing import Optional
import yaml

from .models import ContextRequest, Decision, SENSITIVITY_ORDER


class PolicyEngine:
    def __init__(self, policy_path: str):
        self.policies = self._load(policy_path)

    @classmethod
    def from_list(cls, policies: list) -> "PolicyEngine":
        """Build a PolicyEngine directly from a list of policy dicts (e.g. from DB)."""
        engine = cls.__new__(cls)
        engine.policies = policies
        return engine

    def _load(self, path: str) -> list:
        with open(path) as f:
            data = yaml.safe_load(f)
        return data.get("policies", [])

    def evaluate(self, request: ContextRequest) -> tuple[Decision, str, str]:
        """
        Returns (decision, policy_name, reason).
        Deny-by-default — if no policy matches, access is denied.
        """
        for policy in self.policies:
            if policy["agent_role"] != request.agent_role:
                continue

            # Denied sources take priority
            denied = policy.get("denied_sources", [])
            if request.source_id in denied:
                return (
                    Decision.DENY,
                    policy["name"],
                    f"Source '{request.source_id}' is explicitly denied for role '{request.agent_role}'",
                )

            # Allowed sources — if list exists, source must be in it
            allowed = policy.get("allowed_sources", [])
            if allowed and request.source_id not in allowed:
                return (
                    Decision.DENY,
                    policy["name"],
                    f"Source '{request.source_id}' is not in the allowed list for role '{request.agent_role}'",
                )

            # Sensitivity ceiling check
            max_sensitivity = policy.get("max_sensitivity", "restricted")
            req_level = SENSITIVITY_ORDER.index(request.sensitivity_level.value)
            max_level = SENSITIVITY_ORDER.index(max_sensitivity)
            if req_level > max_level:
                return (
                    Decision.DENY,
                    policy["name"],
                    f"Data sensitivity '{request.sensitivity_level.value}' exceeds ceiling '{max_sensitivity}' for role '{request.agent_role}'",
                )

            return Decision.ALLOW, policy["name"], "Access granted"

        return (
            Decision.DENY,
            "default_deny",
            f"No matching policy for agent role '{request.agent_role}' — denied by default",
        )
