import datetime
import httpx
from typing import Dict, Any, Optional
from .crypto import KeyPair
from .client import AuthevoClient
from .canonicalize import canonicalize
from .config import AuthevoConfig

class AuthevoAgent:
    def __init__(
        self, 
        keypair: KeyPair,
        config: Optional[AuthevoConfig] = None,
        # Deprecated parameters for backward compatibility
        api_url: Optional[str] = None,
        opa_url: Optional[str] = None,
        policy_path: str = "authevo/refund/allow"
    ):
        # Handle backward compatibility
        if config is None:
            if api_url or opa_url:
                config = AuthevoConfig(
                    api_url=api_url or "http://localhost:4000"
                )
                if opa_url:
                    config.opa_url = opa_url
            else:
                config = AuthevoConfig.from_env()
        
        self.keypair = keypair
        self.config = config
        self.client = AuthevoClient(config=config)
        self.opa_url = config.opa_url or "http://localhost:8181"
        self.opa_url = self.opa_url.rstrip('/')
        self.policy_path = policy_path
        self._http_client = httpx.AsyncClient(timeout=config.timeout)

    @classmethod
    def generate(cls, config: Optional[AuthevoConfig] = None, **kwargs):
        """Generate a new agent with a random keypair."""
        return cls(KeyPair.generate(), config=config, **kwargs)

    @classmethod
    def load(cls, private_key_b64: str, config: Optional[AuthevoConfig] = None, **kwargs):
        """Load an agent from a private key."""
        return cls(KeyPair.from_private_key(private_key_b64), config=config, **kwargs)

    async def close(self):
        """Close all HTTP connections."""
        await self.client.close()
        await self._http_client.aclose()
    
    async def __aenter__(self):
        """Async context manager entry."""
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """Async context manager exit."""
        await self.close()
        return False

    async def register(self, name: str, policy_id: str = "refund_v1"):
        """Register the agent with the Authevo API."""
        return await self.client.deploy_agent(
            name=name,
            did=self.keypair.did,
            public_key=self.keypair.public_key_b64,
            policy_id=policy_id
        )

    async def evaluate_policy(self, action: str, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Evaluate OPA policy for the action."""
        # OPA expects POST to /v1/data/<path>
        # Input structure depends on the policy. 
        # Assuming standard structure: input: { action, payload, agent }
        opa_input = {
            "input": {
                "action": action,
                "payload": payload,
                "agent": self.keypair.did
            }
        }
        
        try:
            response = await self._http_client.post(
                f"{self.opa_url}/v1/data/{self.policy_path}",
                json=opa_input
            )
            response.raise_for_status()
            result = response.json().get("result", {})
            
            # If result is boolean (allow/deny)
            if isinstance(result, bool):
                decision = "Permit" if result else "Deny"
                reason = "Policy allowed" if result else "Policy denied"
            else:
                # If result is object (allow, deny, reason, etc)
                # Adjust based on your Rego policy structure
                # For MVP refund policy, it might just return true/false for 'allow'
                # We need to construct the decision object
                allowed = result if isinstance(result, bool) else result.get("allow", False)
                decision = "Permit" if allowed else "Deny"
                reason = result.get("reason", "Policy evaluation") if isinstance(result, dict) else "Policy evaluation"

            return {
                "decision": decision,
                "reason": reason,
                "policyId": self.policy_path
            }
        except Exception as e:
            # Fail closed
            return {
                "decision": "Deny",
                "reason": f"Policy evaluation failed: {str(e)}",
                "policyId": self.policy_path
            }

    async def execute_action(self, action: str, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Execute a verifiable action."""
        # 1. Evaluate Policy
        policy_decision = await self.evaluate_policy(action, payload)
        
        if policy_decision["decision"] == "Deny":
            # We can choose to raise error or submit the denied action for audit
            # For now, let's submit it so it appears in the log as denied
            pass

        # 2. Construct Action Object
        timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat().replace("+00:00", "Z")
        
        action_obj = {
            "agent": self.keypair.did,
            "action": action,
            "payload": payload,
            "timestamp": timestamp,
            "policyDecision": policy_decision
        }

        # 3. Canonicalize
        canonical_bytes = canonicalize(action_obj)

        # 4. Sign
        signature = self.keypair.sign(canonical_bytes)

        # 5. Construct Proof
        full_action = action_obj.copy()
        full_action["proof"] = {
            "type": "Ed25519Signature2020",
            "verificationMethod": f"{self.keypair.did}#key-1",
            "signatureValue": signature
        }

        # 6. Submit
        return await self.client.submit_action(full_action)
