"""
Agent Trust API Client.
"""

from __future__ import annotations

import httpx
from datetime import datetime
from typing import Any, Optional
from urllib.parse import quote

from .models import (
    VerificationResult,
    AgentReputation,
    InteractionResult,
    ScoreBreakdown,
    TextScanResult,
    ThreatMatch,
    Verdict,
    ThreatLevel,
    InteractionOutcome,
)


DEFAULT_API_URL = "https://agent-trust-infrastructure-production.up.railway.app"


class AgentTrustError(Exception):
    """Base exception for Agent Trust SDK."""
    pass


class APIError(AgentTrustError):
    """API returned an error response."""
    def __init__(self, message: str, status_code: int = None, response: dict = None):
        super().__init__(message)
        self.status_code = status_code
        self.response = response


class AgentTrustClient:
    """
    Client for the Agent Trust Verification API.
    
    Args:
        api_url: Base URL for the API (default: production URL)
        timeout: Request timeout in seconds (default: 30)
        api_key: API key for authentication (optional, for future use)
    
    Example:
        client = AgentTrustClient()
        
        # Verify an agent
        result = client.verify_agent(
            name="My Agent",
            url="https://example.com/agent"
        )
        
        if result.is_blocked:
            print(f"Blocked: {result.reasoning}")
    """
    
    def __init__(
        self,
        api_url: str = DEFAULT_API_URL,
        timeout: float = 30.0,
        api_key: Optional[str] = None,
    ):
        self.api_url = api_url.rstrip("/")
        self.timeout = timeout
        self.api_key = api_key
        self._client = httpx.Client(timeout=timeout)
    
    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.close()
    
    def close(self):
        """Close the HTTP client."""
        self._client.close()
    
    def _request(self, method: str, path: str, **kwargs) -> dict:
        """Make an HTTP request to the API."""
        url = f"{self.api_url}{path}"
        
        headers = kwargs.pop("headers", {})
        if self.api_key:
            headers["Authorization"] = f"Bearer {self.api_key}"
        
        response = self._client.request(method, url, headers=headers, **kwargs)
        
        if response.status_code >= 400:
            try:
                error_data = response.json()
                message = error_data.get("message", error_data.get("detail", "Unknown error"))
            except Exception:
                message = response.text or f"HTTP {response.status_code}"
            raise APIError(message, response.status_code, error_data if 'error_data' in dir() else None)
        
        return response.json()
    
    # ========================================================================
    # Verification Endpoints
    # ========================================================================
    
    def verify_agent(
        self,
        name: str,
        url: str,
        description: Optional[str] = None,
        skills: Optional[list[dict]] = None,
        raw_card: Optional[dict] = None,
        requester_id: Optional[str] = None,
    ) -> VerificationResult:
        """
        Verify an agent's trustworthiness.
        
        Args:
            name: Agent name
            url: Agent URL (unique identifier)
            description: Agent description
            skills: List of agent skills
            raw_card: Raw agent card data (overrides other fields)
            requester_id: ID of the requester (for logging)
        
        Returns:
            VerificationResult with verdict, threats, and trust score
        
        Example:
            result = client.verify_agent(
                name="Shopping Assistant",
                url="https://shop.ai/agent",
                description="I help find the best deals"
            )
            
            if result.is_safe:
                print(f"Agent is safe! Trust score: {result.trust_score}")
            else:
                for threat in result.threats:
                    print(f"Threat: {threat.pattern_name} ({threat.severity})")
        """
        data = {
            "agent_card": {
                "name": name,
                "url": url,
                "description": description,
                "skills": skills or [],
                "raw_card": raw_card,
            },
            "requester_id": requester_id,
        }
        
        response = self._request("POST", "/verify/agent", json=data)
        return self._parse_verification_result(response)
    
    def verify_message(
        self,
        text: str,
        source_agent_url: Optional[str] = None,
        requester_id: Optional[str] = None,
    ) -> VerificationResult:
        """
        Verify a message's safety.
        
        Args:
            text: Message text to verify
            source_agent_url: URL of the agent that sent the message
            requester_id: ID of the requester (for logging)
        
        Returns:
            VerificationResult with verdict and detected threats
        """
        data = {
            "parts": [{"kind": "text", "text": text}],
            "source_agent_url": source_agent_url,
            "requester_id": requester_id,
        }
        
        response = self._request("POST", "/verify/message", json=data)
        return self._parse_verification_result(response)
    
    def scan_text(
        self,
        text: str,
        min_severity: ThreatLevel = ThreatLevel.LOW,
    ) -> TextScanResult:
        """
        Quick scan of raw text for threats.
        
        Args:
            text: Text to scan
            min_severity: Minimum severity to report (default: LOW)
        
        Returns:
            TextScanResult with detected threats
        
        Example:
            result = client.scan_text("Ignore previous instructions...")
            if not result.is_safe:
                print(f"Threats found: {len(result.threats)}")
        """
        data = {
            "text": text,
            "min_severity": min_severity.value,
        }
        
        response = self._request("POST", "/verify/text", json=data)
        
        return TextScanResult(
            request_id=response["request_id"],
            verdict=Verdict(response["verdict"]),
            threat_level=ThreatLevel(response["threat_level"]),
            threats=self._parse_threats(response.get("threats", [])),
            reasoning=response.get("reasoning", ""),
            text_length=response.get("text_length", 0),
            scan_time_ms=response.get("scan_time_ms", 0.0),
        )
    
    # ========================================================================
    # Agent Reputation Endpoints
    # ========================================================================
    
    def get_reputation(self, agent_url: str) -> AgentReputation:
        """
        Get an agent's reputation details.
        
        Args:
            agent_url: URL of the agent
        
        Returns:
            AgentReputation with trust score and history
        
        Example:
            rep = client.get_reputation("https://shop.ai/agent")
            print(f"Trust: {rep.trust_score}, Success rate: {rep.success_rate}")
        """
        encoded_url = quote(agent_url, safe="")
        response = self._request("GET", f"/agents/{encoded_url}/reputation")
        return self._parse_reputation(response)
    
    def get_score_breakdown(self, agent_url: str) -> ScoreBreakdown:
        """
        Get detailed breakdown of an agent's trust score.
        
        Args:
            agent_url: URL of the agent
        
        Returns:
            ScoreBreakdown with all score components
        """
        encoded_url = quote(agent_url, safe="")
        response = self._request("GET", f"/agents/{encoded_url}/score-breakdown")
        
        return ScoreBreakdown(
            base_score=response["base_score"],
            interaction_score=response["interaction_score"],
            report_penalty=response["report_penalty"],
            verification_bonus=response["verification_bonus"],
            time_decay=response["time_decay"],
            final_score=response["final_score"],
            total_interactions=response.get("total_interactions", 0),
            success_rate=response.get("success_rate"),
            days_since_last_interaction=response.get("days_since_last_interaction"),
        )
    
    def report_interaction(
        self,
        agent_url: str,
        outcome: InteractionOutcome,
        task_type: Optional[str] = None,
        description: Optional[str] = None,
        response_quality: Optional[int] = None,
        task_completed: Optional[bool] = None,
        followed_instructions: Optional[bool] = None,
        reporter_id: Optional[str] = None,
    ) -> InteractionResult:
        """
        Report an interaction with an agent.
        
        Args:
            agent_url: URL of the agent
            outcome: SUCCESS, FAILURE, or NEUTRAL
            task_type: Type of task (e.g., "shopping", "research")
            description: Brief description of the interaction
            response_quality: Rating 1-5
            task_completed: Whether the task was completed
            followed_instructions: Whether agent followed instructions
            reporter_id: ID of the reporter
        
        Returns:
            InteractionResult with score impact
        
        Example:
            result = client.report_interaction(
                agent_url="https://shop.ai/agent",
                outcome=InteractionOutcome.SUCCESS,
                task_type="shopping",
                response_quality=5,
                task_completed=True
            )
            print(f"Score changed by {result.score_delta}")
        """
        encoded_url = quote(agent_url, safe="")
        data = {
            "outcome": outcome.value,
            "task_type": task_type,
            "description": description,
            "response_quality": response_quality,
            "task_completed": task_completed,
            "followed_instructions": followed_instructions,
            "reporter_id": reporter_id,
        }
        # Remove None values
        data = {k: v for k, v in data.items() if v is not None}
        
        response = self._request("POST", f"/agents/{encoded_url}/interactions", json=data)
        
        return InteractionResult(
            interaction_id=response["interaction_id"],
            agent_url=response["agent_url"],
            outcome=InteractionOutcome(response["outcome"]),
            score_delta=response["score_delta"],
            new_trust_score=response["new_trust_score"],
            message=response["message"],
            created_at=self._parse_datetime(response.get("created_at")),
        )
    
    # ========================================================================
    # Threat Reporting
    # ========================================================================
    
    def report_threat(
        self,
        agent_url: str,
        threat_type: str,
        description: str,
        evidence: Optional[str] = None,
        interaction_data: Optional[dict] = None,
        reporter_id: Optional[str] = None,
    ) -> dict:
        """
        Report a threat or suspicious agent behavior.
        
        Args:
            agent_url: URL of the suspicious agent
            threat_type: Type of threat (e.g., "prompt_injection", "data_exfiltration")
            description: Description of the threat
            evidence: Evidence supporting the report
            interaction_data: Data from the interaction
            reporter_id: ID of the reporter
        
        Returns:
            Report confirmation with ID
        """
        data = {
            "agent_url": agent_url,
            "threat_type": threat_type,
            "description": description,
            "evidence": evidence,
            "interaction_data": interaction_data,
            "reporter_id": reporter_id,
        }
        data = {k: v for k, v in data.items() if v is not None}
        
        return self._request("POST", "/threats/report", json=data)
    
    # ========================================================================
    # Agent Verification Endpoints
    # ========================================================================
    
    def start_email_verification(self, agent_url: str, email: str) -> dict:
        """
        Start email verification for an agent.
        
        Initiates the email verification process by sending a verification
        token to the specified email address.
        
        Args:
            agent_url: URL of the agent to verify
            email: Email address to verify ownership of
        
        Returns:
            dict with verification token info:
                - message: Status message
                - email: The email address being verified
                - expires_at: When the token expires (ISO format)
                - token: The verification token (MVP only, for testing)
        
        Example:
            result = client.start_email_verification(
                agent_url="https://myagent.ai/agent",
                email="owner@myagent.ai"
            )
            print(f"Check your email! Token expires: {result['expires_at']}")
        """
        encoded_url = quote(agent_url, safe="")
        data = {"email": email}
        return self._request("POST", f"/agents/{encoded_url}/verify/email", json=data)
    
    def confirm_email_verification(self, agent_url: str, token: str) -> dict:
        """
        Confirm email verification with the token.
        
        Completes the email verification process using the token received
        via email (or returned from start_email_verification in MVP mode).
        
        Args:
            agent_url: URL of the agent being verified
            token: The verification token (64-character hex string)
        
        Returns:
            dict with confirmation result:
                - success: Whether verification succeeded
                - message: Status message
                - agent_url: The verified agent URL
                - verified_email: The email that was verified
                - verified_at: When verification completed (ISO format)
        
        Example:
            result = client.confirm_email_verification(
                agent_url="https://myagent.ai/agent",
                token="abc123..."
            )
            if result['success']:
                print(f"Email verified: {result['verified_email']}")
        """
        encoded_url = quote(agent_url, safe="")
        return self._request("GET", f"/agents/{encoded_url}/verify/email/{token}")
    
    def start_domain_verification(self, agent_url: str) -> dict:
        """
        Start domain verification for an agent.
        
        Generates a DNS TXT record challenge that must be added to the
        agent's domain to prove ownership.
        
        Args:
            agent_url: URL of the agent to verify
        
        Returns:
            dict with DNS record instructions:
                - message: Status message
                - domain: The domain being verified
                - record_type: DNS record type (TXT)
                - record_name: Where to add the record (e.g., _trustagents.domain.com)
                - record_value: The value to set (e.g., trustagents-verify=<token>)
                - instructions: Human-readable setup instructions
                - expires_at: When the challenge expires (ISO format)
        
        Example:
            result = client.start_domain_verification(
                agent_url="https://myagent.ai/agent"
            )
            print(f"Add this DNS record:")
            print(f"  Type: {result['record_type']}")
            print(f"  Name: {result['record_name']}")
            print(f"  Value: {result['record_value']}")
        """
        encoded_url = quote(agent_url, safe="")
        return self._request("POST", f"/agents/{encoded_url}/verify/domain")
    
    def check_domain_verification(self, agent_url: str) -> dict:
        """
        Check if domain verification DNS record exists.
        
        Performs a DNS lookup to verify that the required TXT record
        has been properly configured.
        
        Args:
            agent_url: URL of the agent being verified
        
        Returns:
            dict with verification status:
                - verified: Whether the domain is verified
                - message: Status message
                - domain: The domain being checked
                - expected_record: The expected DNS record value
                - found_records: List of TXT records found (if any)
                - verified_at: When verification completed (if verified)
        
        Example:
            result = client.check_domain_verification(
                agent_url="https://myagent.ai/agent"
            )
            if result['verified']:
                print(f"Domain verified at {result['verified_at']}")
            else:
                print(f"Record not found. Expected: {result['expected_record']}")
        """
        encoded_url = quote(agent_url, safe="")
        return self._request("GET", f"/agents/{encoded_url}/verify/domain/check")
    
    # ========================================================================
    # Utility Methods
    # ========================================================================
    
    def health_check(self) -> dict:
        """Check API health status."""
        return self._request("GET", "/health")
    
    def get_stats(self) -> dict:
        """Get API statistics."""
        return self._request("GET", "/stats")
    
    # ========================================================================
    # Private Helpers
    # ========================================================================
    
    def _parse_verification_result(self, data: dict) -> VerificationResult:
        """Parse verification response into VerificationResult."""
        return VerificationResult(
            request_id=data["request_id"],
            verdict=Verdict(data["verdict"]),
            threat_level=ThreatLevel(data["threat_level"]),
            threats=self._parse_threats(data.get("threats", [])),
            reasoning=data.get("reasoning", ""),
            trust_score=data.get("trust_score"),
            checked_at=self._parse_datetime(data.get("checked_at")),
            agent_url=data.get("agent_url"),
            agent_name=data.get("agent_name"),
            is_registered=data.get("is_registered", False),
            is_verified=data.get("is_verified", False),
            reputation_score=data.get("reputation_score"),
        )
    
    def _parse_reputation(self, data: dict) -> AgentReputation:
        """Parse reputation response into AgentReputation."""
        return AgentReputation(
            agent_url=data["agent_url"],
            trust_score=data["trust_score"],
            is_registered=data["is_registered"],
            is_verified=data["is_verified"],
            agent_name=data.get("agent_name"),
            first_seen=self._parse_datetime(data.get("first_seen")),
            last_seen=self._parse_datetime(data.get("last_seen")),
            total_interactions=data.get("total_interactions", 0),
            successful_interactions=data.get("successful_interactions", 0),
            failed_interactions=data.get("failed_interactions", 0),
            reports_against=data.get("reports_against", 0),
            score_breakdown=data.get("score_breakdown"),
        )
    
    def _parse_threats(self, threats: list[dict]) -> list[ThreatMatch]:
        """Parse threat list into ThreatMatch objects."""
        return [
            ThreatMatch(
                pattern_id=t["pattern_id"],
                pattern_name=t["pattern_name"],
                severity=ThreatLevel(t["severity"]),
                location=t["location"],
                matched_text=t.get("matched_text"),
                description=t.get("description"),
            )
            for t in threats
        ]
    
    def _parse_datetime(self, value: Optional[str]) -> Optional[datetime]:
        """Parse ISO datetime string."""
        if not value:
            return None
        try:
            # Handle both with and without microseconds
            if "." in value:
                return datetime.fromisoformat(value.replace("Z", "+00:00"))
            return datetime.fromisoformat(value)
        except ValueError:
            return None


# Async client for async/await usage
class AsyncAgentTrustClient:
    """
    Async client for the Agent Trust Verification API.
    
    Same interface as AgentTrustClient but uses async/await.
    
    Example:
        async with AsyncAgentTrustClient() as client:
            result = await client.verify_agent(...)
    """
    
    def __init__(
        self,
        api_url: str = DEFAULT_API_URL,
        timeout: float = 30.0,
        api_key: Optional[str] = None,
    ):
        self.api_url = api_url.rstrip("/")
        self.timeout = timeout
        self.api_key = api_key
        self._client = httpx.AsyncClient(timeout=timeout)
    
    async def __aenter__(self):
        return self
    
    async def __aexit__(self, *args):
        await self.close()
    
    async def close(self):
        """Close the HTTP client."""
        await self._client.aclose()
    
    async def _request(self, method: str, path: str, **kwargs) -> dict:
        """Make an async HTTP request to the API."""
        url = f"{self.api_url}{path}"
        
        headers = kwargs.pop("headers", {})
        if self.api_key:
            headers["Authorization"] = f"Bearer {self.api_key}"
        
        response = await self._client.request(method, url, headers=headers, **kwargs)
        
        if response.status_code >= 400:
            try:
                error_data = response.json()
                message = error_data.get("message", error_data.get("detail", "Unknown error"))
            except Exception:
                message = response.text or f"HTTP {response.status_code}"
            raise APIError(message, response.status_code)
        
        return response.json()
    
    # All methods have the same signature as sync client but are async
    async def verify_agent(self, *args, **kwargs) -> VerificationResult:
        """Verify an agent's trustworthiness."""
        # Reuse sync client's logic
        sync = AgentTrustClient.__new__(AgentTrustClient)
        sync.api_url = self.api_url
        
        data = {
            "agent_card": {
                "name": kwargs.get("name") or args[0],
                "url": kwargs.get("url") or args[1],
                "description": kwargs.get("description"),
                "skills": kwargs.get("skills") or [],
                "raw_card": kwargs.get("raw_card"),
            },
            "requester_id": kwargs.get("requester_id"),
        }
        
        response = await self._request("POST", "/verify/agent", json=data)
        return sync._parse_verification_result(response)
    
    async def scan_text(self, text: str, min_severity: ThreatLevel = ThreatLevel.LOW) -> TextScanResult:
        """Quick scan of raw text for threats."""
        data = {"text": text, "min_severity": min_severity.value}
        response = await self._request("POST", "/verify/text", json=data)
        
        sync = AgentTrustClient.__new__(AgentTrustClient)
        return TextScanResult(
            request_id=response["request_id"],
            verdict=Verdict(response["verdict"]),
            threat_level=ThreatLevel(response["threat_level"]),
            threats=sync._parse_threats(response.get("threats", [])),
            reasoning=response.get("reasoning", ""),
            text_length=response.get("text_length", 0),
            scan_time_ms=response.get("scan_time_ms", 0.0),
        )
    
    async def get_reputation(self, agent_url: str) -> AgentReputation:
        """Get an agent's reputation details."""
        encoded_url = quote(agent_url, safe="")
        response = await self._request("GET", f"/agents/{encoded_url}/reputation")
        sync = AgentTrustClient.__new__(AgentTrustClient)
        return sync._parse_reputation(response)
    
    async def health_check(self) -> dict:
        """Check API health status."""
        return await self._request("GET", "/health")
    
    async def start_email_verification(self, agent_url: str, email: str) -> dict:
        """
        Start email verification for an agent.
        
        Args:
            agent_url: URL of the agent to verify
            email: Email address to verify ownership of
        
        Returns:
            dict with verification token info
        """
        encoded_url = quote(agent_url, safe="")
        data = {"email": email}
        return await self._request("POST", f"/agents/{encoded_url}/verify/email", json=data)
    
    async def confirm_email_verification(self, agent_url: str, token: str) -> dict:
        """
        Confirm email verification with the token.
        
        Args:
            agent_url: URL of the agent being verified
            token: The verification token
        
        Returns:
            dict with confirmation result
        """
        encoded_url = quote(agent_url, safe="")
        return await self._request("GET", f"/agents/{encoded_url}/verify/email/{token}")
    
    async def start_domain_verification(self, agent_url: str) -> dict:
        """
        Start domain verification for an agent.
        
        Args:
            agent_url: URL of the agent to verify
        
        Returns:
            dict with DNS record instructions
        """
        encoded_url = quote(agent_url, safe="")
        return await self._request("POST", f"/agents/{encoded_url}/verify/domain")
    
    async def check_domain_verification(self, agent_url: str) -> dict:
        """
        Check if domain verification DNS record exists.
        
        Args:
            agent_url: URL of the agent being verified
        
        Returns:
            dict with verification status
        """
        encoded_url = quote(agent_url, safe="")
        return await self._request("GET", f"/agents/{encoded_url}/verify/domain/check")
