from __future__ import annotations

from typing import Any, Callable

from ._config import SurfinguardConfig
from ._http import SurfinguardHTTPClient
from ._telemetry import report_telemetry
from ._version import __version__
from .decorators import _make_protect_decorator
from .enums import Policy, RiskLevel
from .exceptions import NotAllowedError
from .models import BatchResult, CheckResult, HealthResponse, SessionInfo


class Guard:
    """Main entry point for the Surfinguard Python SDK.

    Args:
        api_key: Your Surfinguard API key (sg_live_... or sg_test_...).
        base_url: API base URL. Defaults to https://api.surfinguard.com.
        policy: Enforcement policy. PERMISSIVE, MODERATE (default), or STRICT.
        environment: 'live' or 'test'.
        timeout: HTTP request timeout in seconds.
        local_only: If True, raises NotImplementedError (reserved for future local engine).
        session_id: Optional session ID for multi-step chain detection.
        agent_id: Optional agent identifier.
    """

    def __init__(
        self,
        api_key: str,
        *,
        base_url: str = "https://api.surfinguard.com",
        policy: Policy = Policy.MODERATE,
        environment: str = "live",
        timeout: float = 5.0,
        local_only: bool = False,
        session_id: str | None = None,
        agent_id: str | None = None,
        telemetry: bool = False,
    ) -> None:
        if local_only:
            raise NotImplementedError(
                "Local-only mode is not yet supported in the Python SDK. "
                "Use api_key with the hosted API."
            )

        self._config = SurfinguardConfig(
            api_key=api_key,
            base_url=base_url,
            policy=policy,
            environment=environment,
            timeout=timeout,
            local_only=local_only,
        )
        self._http = SurfinguardHTTPClient(self._config)
        self._session_id = session_id
        self._agent_id = agent_id
        self._telemetry = telemetry

    def check_url(self, url: str) -> CheckResult:
        """Check a URL for phishing, scams, and malware."""
        data = self._http.post("/v2/check/url", {"url": url})
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        self._report_telemetry("url", url, result)
        return result

    def check_command(self, command: str) -> CheckResult:
        """Check a shell command for destructive or malicious operations."""
        data = self._http.post("/v2/check/command", {"command": command})
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        self._report_telemetry("command", command, result)
        return result

    def check_text(self, text: str) -> CheckResult:
        """Check text for prompt injection attempts."""
        data = self._http.post("/v2/check/text", {"text": text})
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        self._report_telemetry("text", text, result)
        return result

    def check_file_read(self, path: str) -> CheckResult:
        """Check a file read path for sensitive data access."""
        data = self._http.post("/v2/check/file", {"path": path, "operation": "read"})
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_file_write(self, path: str, content: str | None = None) -> CheckResult:
        """Check a file write operation for destructive or persistence risks."""
        payload: dict[str, Any] = {"path": path, "operation": "write"}
        if content is not None:
            payload["content"] = content
        data = self._http.post("/v2/check/file", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check(self, action_type: str, value: str, **metadata: Any) -> CheckResult:
        """Universal check — pass any action type and value."""
        payload: dict[str, Any] = {"type": action_type, "value": value}
        if metadata:
            payload["metadata"] = metadata
        data = self._http.post("/v2/check", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_api_call(
        self,
        url: str,
        method: str = "GET",
        headers: dict[str, str] | None = None,
        body: str | None = None,
    ) -> CheckResult:
        """Check an API call for dangerous patterns (SSRF, credential forwarding, etc.)."""
        payload: dict[str, Any] = {"url": url, "method": method}
        if headers is not None:
            payload["headers"] = headers
        if body is not None:
            payload["body"] = body
        data = self._http.post("/v2/check/api-call", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_query(self, query: str) -> CheckResult:
        """Check a SQL query for destructive, exfiltration, or escalation patterns."""
        data = self._http.post("/v2/check/query", {"query": query})
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_code(self, code: str, language: str | None = None) -> CheckResult:
        """Check generated code for backdoors, vulnerabilities, and malicious patterns."""
        payload: dict[str, Any] = {"code": code}
        if language is not None:
            payload["language"] = language
        data = self._http.post("/v2/check/code", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_message(
        self,
        body: str,
        *,
        channel: str | None = None,
        to: str | None = None,
        subject: str | None = None,
    ) -> CheckResult:
        """Check a message/notification for data exfiltration or impersonation."""
        payload: dict[str, Any] = {"body": body}
        if channel is not None:
            payload["channel"] = channel
        if to is not None:
            payload["to"] = to
        if subject is not None:
            payload["subject"] = subject
        data = self._http.post("/v2/check/message", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_transaction(
        self,
        description: str,
        *,
        amount: float | None = None,
        currency: str | None = None,
        recipient: str | None = None,
        type: str | None = None,
    ) -> CheckResult:
        """Check a financial transaction for unauthorized purchases, transfers, or trades."""
        payload: dict[str, Any] = {"description": description}
        if amount is not None:
            payload["amount"] = amount
        if currency is not None:
            payload["currency"] = currency
        if recipient is not None:
            payload["recipient"] = recipient
        if type is not None:
            payload["type"] = type
        data = self._http.post("/v2/check/transaction", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_auth(
        self,
        action: str,
        *,
        scope: str | None = None,
        role: str | None = None,
        target: str | None = None,
    ) -> CheckResult:
        """Check an auth/identity action for privilege escalation or credential exposure."""
        payload: dict[str, Any] = {"action": action}
        if scope is not None:
            payload["scope"] = scope
        if role is not None:
            payload["role"] = role
        if target is not None:
            payload["target"] = target
        data = self._http.post("/v2/check/auth", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_git(
        self,
        command: str,
        *,
        branch: str | None = None,
        remote: str | None = None,
        files: list[str] | None = None,
    ) -> CheckResult:
        """Check a git operation for force push, unauthorized merge, CI/CD modification, etc."""
        payload: dict[str, Any] = {"command": command}
        if branch is not None:
            payload["branch"] = branch
        if remote is not None:
            payload["remote"] = remote
        if files is not None:
            payload["files"] = files
        data = self._http.post("/v2/check/git", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_ui_action(
        self,
        action: str,
        *,
        element: str | None = None,
        url: str | None = None,
        application: str | None = None,
    ) -> CheckResult:
        """Check a UI/browser action for destructive clicks, payment forms, screen capture, etc."""
        payload: dict[str, Any] = {"action": action}
        if element is not None:
            payload["element"] = element
        if url is not None:
            payload["url"] = url
        if application is not None:
            payload["application"] = application
        data = self._http.post("/v2/check/ui-action", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_infra(
        self,
        action: str,
        *,
        provider: str | None = None,
        environment: str | None = None,
        resource: str | None = None,
    ) -> CheckResult:
        """Check an infrastructure/cloud action for container escape, IaC changes, IAM escalation, etc."""
        payload: dict[str, Any] = {"action": action}
        if provider is not None:
            payload["provider"] = provider
        if environment is not None:
            payload["environment"] = environment
        if resource is not None:
            payload["resource"] = resource
        data = self._http.post("/v2/check/infra", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_agent_comm(
        self,
        action: str,
        *,
        agent_id: str | None = None,
        target_agent: str | None = None,
        tool: str | None = None,
    ) -> CheckResult:
        """Check agent communication for malicious delegation, context poisoning, or tool abuse."""
        payload: dict[str, Any] = {"action": action}
        if agent_id is not None:
            payload["agent_id"] = agent_id
        if target_agent is not None:
            payload["target_agent"] = target_agent
        if tool is not None:
            payload["tool"] = tool
        data = self._http.post("/v2/check/agent-comm", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_data_pipeline(
        self,
        action: str,
        *,
        operation: str | None = None,
        dataset: str | None = None,
        model: str | None = None,
    ) -> CheckResult:
        """Check a data/ML pipeline action for ETL modification, model poisoning, or data exfiltration."""
        payload: dict[str, Any] = {"action": action}
        if operation is not None:
            payload["operation"] = operation
        if dataset is not None:
            payload["dataset"] = dataset
        if model is not None:
            payload["model"] = model
        data = self._http.post("/v2/check/data-pipeline", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_document(
        self,
        action: str,
        *,
        operation: str | None = None,
        content: str | None = None,
        recipient: str | None = None,
    ) -> CheckResult:
        """Check a document operation for contract tampering, financial modification, or external sharing."""
        payload: dict[str, Any] = {"action": action}
        if operation is not None:
            payload["operation"] = operation
        if content is not None:
            payload["content"] = content
        if recipient is not None:
            payload["recipient"] = recipient
        data = self._http.post("/v2/check/document", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def check_iot(
        self,
        command: str,
        *,
        device_type: str | None = None,
        device_id: str | None = None,
        resource: str | None = None,
    ) -> CheckResult:
        """Check an IoT/physical device command for lock manipulation, industrial control, or vehicle injection."""
        payload: dict[str, Any] = {"command": command}
        if device_type is not None:
            payload["device_type"] = device_type
        if device_id is not None:
            payload["device_id"] = device_id
        if resource is not None:
            payload["resource"] = resource
        data = self._http.post("/v2/check/iot", payload)
        result = CheckResult.model_validate(data)
        self._enforce_policy(result)
        return result

    def health(self) -> HealthResponse:
        """Check API health and version."""
        data = self._http.get("/v2/health")
        return HealthResponse.model_validate(data)

    def check_batch(
        self,
        actions: list[dict[str, Any]],
        *,
        session_id: str | None = None,
        agent_id: str | None = None,
        sequential: bool = True,
        enhance: bool = False,
    ) -> BatchResult:
        """Check multiple actions in a batch with session tracking and chain detection."""
        payload: dict[str, Any] = {"actions": actions, "sequential": sequential}
        sid = session_id or self._session_id
        if sid:
            payload["sessionId"] = sid
        aid = agent_id or self._agent_id
        if aid:
            payload["agentId"] = aid
        if enhance:
            payload["enhance"] = True
        data = self._http.post("/v2/check/batch", payload)
        return BatchResult.model_validate(data)

    def get_session(self, session_id: str | None = None) -> SessionInfo:
        """Get session info."""
        sid = session_id or self._session_id
        if not sid:
            raise ValueError("No session_id provided")
        data = self._http.get(f"/v2/sessions/{sid}")
        return SessionInfo.model_validate(data)

    def reset_session(self, session_id: str | None = None) -> None:
        """End/invalidate a session."""
        sid = session_id or self._session_id
        if not sid:
            raise ValueError("No session_id provided")
        self._http.delete(f"/v2/sessions/{sid}")

    def create_policy(self, name: str, level: str = "balanced", **kwargs: Any) -> dict[str, Any]:
        """Create a new policy."""
        payload: dict[str, Any] = {"name": name, "level": level, **kwargs}
        return self._http.post("/v2/policies", payload)

    def list_policies(self) -> dict[str, Any]:
        """List policies."""
        return self._http.get("/v2/policies")

    def activate_policy(self, policy_id: str) -> dict[str, Any]:
        """Activate a policy."""
        return self._http.post(f"/v2/policies/{policy_id}/activate", {})

    def protect(self, action_type: str = "command") -> Callable[..., Any]:
        """Decorator that checks the first argument of a function before execution.

        Args:
            action_type: The action type to check ('url', 'command', 'text', etc.)

        Returns:
            A decorator function.
        """
        return _make_protect_decorator(self, action_type)

    def assess_compliance(self, profile: dict[str, Any]) -> dict[str, Any]:
        """Assess an agent profile against the EU AI Act compliance framework.

        Args:
            profile: Agent profile dict with keys: name, domain, autonomyLevel,
                     and optionally description, usesPersonalData, affectsSafety,
                     makesDecisions, interactsWithPublic.

        Returns:
            ComplianceReport dict with framework, riskClassification, requirements, etc.
        """
        return self._http.post("/v2/compliance/assess", {"agentProfile": profile})

    def _report_telemetry(self, action_type: str, value: str, result: CheckResult) -> None:
        """Report telemetry if enabled (fire-and-forget)."""
        if self._telemetry:
            report_telemetry(self._http, action_type, value, result, __version__)

    def _enforce_policy(self, result: CheckResult) -> None:
        """Enforce the configured policy. Raises NotAllowedError if blocked."""
        policy = self._config.policy

        if policy == Policy.PERMISSIVE:
            return

        if policy == Policy.MODERATE and result.level == RiskLevel.DANGER:
            raise NotAllowedError(result)

        if policy == Policy.STRICT and result.level in (RiskLevel.CAUTION, RiskLevel.DANGER):
            raise NotAllowedError(result)

    def close(self) -> None:
        """Close the underlying HTTP client."""
        self._http.close()

    def __enter__(self) -> Guard:
        return self

    def __exit__(self, *args: Any) -> None:
        self.close()
