"""
Vaikora LangChain Integration.

Drop-in integration with LangChain's tool system. Wraps any LangChain tool
with Vaikora's InterceptorProxy for policy enforcement, PII detection,
and human-in-the-loop approval.

Usage:
    from vaikora.integrations.langchain import VaikoraTool, vaikora_tool

    # Option 1: Wrap an existing tool
    safe_tool = VaikoraTool.from_tool(
        tool=my_dangerous_tool,
        client=vaikora_client,
        action_type="database.write",
    )

    # Option 2: Decorator
    @vaikora_tool(client=vaikora_client, action_type="database.delete")
    def delete_records(table: str, where: str) -> str:
        '''Delete records from a database table.'''
        db.execute(f"DELETE FROM {table} WHERE {where}")
        return f"Deleted records from {table}"
"""
import asyncio
import functools
import logging
from typing import Any, Callable, Optional, Type

logger = logging.getLogger("vaikora.integrations.langchain")


try:
    from langchain_core.tools import BaseTool, ToolException
    from pydantic import BaseModel

    LANGCHAIN_AVAILABLE = True
except ImportError:
    LANGCHAIN_AVAILABLE = False
    BaseTool = object  # type: ignore
    ToolException = Exception  # type: ignore
    BaseModel = object  # type: ignore


class VaikoraTool(BaseTool if LANGCHAIN_AVAILABLE else object):  # type: ignore
    """
    A LangChain tool wrapped with Vaikora security layer.

    Before executing the underlying tool, Vaikora evaluates:
    - Keyword/content policies
    - PII detection
    - OPA/Rego rules
    - Time-of-day restrictions
    - Rate limits

    If the action is blocked, a ToolException is raised so the LangChain
    agent gracefully handles it (e.g., "I am not authorized to do that").

    If human approval is required, the tool pauses until an admin approves
    or the request times out.
    """

    name: str = "vaikora_protected_tool"
    description: str = "A Vaikora-protected tool"

    # Vaikora config
    vaikora_client: Any = None  # VaikoraClient or VaikoraSyncClient
    vaikora_action_type: str = "tool.execute"
    vaikora_resource: Optional[str] = None
    vaikora_require_approval: bool = False
    vaikora_approval_timeout: int = 300  # 5 minutes
    vaikora_check_pii: bool = True

    # The wrapped tool function
    _wrapped_fn: Optional[Callable] = None
    _wrapped_tool: Any = None  # Original BaseTool if wrapping

    class Config:
        arbitrary_types_allowed = True

    def _run(self, *args: Any, **kwargs: Any) -> str:
        """Synchronous execution with Vaikora interception."""
        if not self.vaikora_client:
            raise ToolException("VaikoraTool: No Vaikora client configured")

        # Build the input payload for policy evaluation
        tool_input = {
            "tool_name": self.name,
            "args": [str(a) for a in args],
            **{k: str(v) for k, v in kwargs.items()},
        }

        # Merge all text content for keyword matching
        all_text = " ".join(str(v) for v in tool_input.values())
        tool_input["message"] = all_text
        tool_input["content"] = all_text

        try:
            # Use the sync client's intercept method
            from vaikora import VaikoraSyncClient
            from vaikora.interceptor import InterceptorConfig

            interceptor = self.vaikora_client.interceptor(
                action_type=self.vaikora_action_type,
                resource=self.vaikora_resource,
                check_pii=self.vaikora_check_pii,
                require_approval=self.vaikora_require_approval,
                approval_timeout_seconds=self.vaikora_approval_timeout,
            )

            # Wrap the actual execution
            if self._wrapped_fn:
                wrapped = interceptor(self._wrapped_fn)
                result = wrapped(*args, **kwargs)
            elif self._wrapped_tool:
                wrapped = interceptor(self._wrapped_tool._run)
                result = wrapped(*args, **kwargs)
            else:
                raise ToolException("VaikoraTool: No wrapped function or tool configured")

            if hasattr(result, "success") and not result.success:
                # Interceptor blocked it
                violation = getattr(result, "policy_violation", "Policy violation")
                raise ToolException(
                    f"[Vaikora Security] Action blocked: {violation}"
                )

            return getattr(result, "result", str(result))

        except ToolException:
            raise
        except Exception as e:
            error_str = str(e)
            if "PolicyDenied" in error_str or "blocked" in error_str.lower():
                raise ToolException(f"[Vaikora Security] {error_str}")
            if "ApprovalRequired" in error_str:
                raise ToolException(
                    f"[Vaikora Security] This action requires human approval. "
                    f"Please wait for an administrator to approve."
                )
            if "ApprovalDenied" in error_str:
                raise ToolException(
                    f"[Vaikora Security] A human administrator denied this action."
                )
            raise ToolException(f"[Vaikora Security] Interception error: {error_str}")

    async def _arun(self, *args: Any, **kwargs: Any) -> str:
        """Async execution with Vaikora interception."""
        # For now, delegate to sync version in a thread
        return await asyncio.get_event_loop().run_in_executor(
            None, lambda: self._run(*args, **kwargs)
        )

    @classmethod
    def from_tool(
        cls,
        tool: Any,  # BaseTool
        client: Any,  # VaikoraClient or VaikoraSyncClient
        action_type: str = "tool.execute",
        resource: Optional[str] = None,
        require_approval: bool = False,
        check_pii: bool = True,
    ) -> "VaikoraTool":
        """
        Wrap an existing LangChain BaseTool with Vaikora security.

        Args:
            tool: The original LangChain tool to protect
            client: Vaikora SDK client instance
            action_type: Action type for policy evaluation (e.g., "database.delete")
            resource: Resource identifier (e.g., "production-db")
            require_approval: Whether to require human approval
            check_pii: Whether to scan for PII in inputs
        """
        return cls(
            name=tool.name,
            description=f"[Vaikora Protected] {tool.description}",
            vaikora_client=client,
            vaikora_action_type=action_type,
            vaikora_resource=resource,
            vaikora_require_approval=require_approval,
            vaikora_check_pii=check_pii,
            _wrapped_tool=tool,
        )

    @classmethod
    def from_function(
        cls,
        fn: Callable,
        name: str,
        description: str,
        client: Any,
        action_type: str = "tool.execute",
        resource: Optional[str] = None,
        require_approval: bool = False,
        check_pii: bool = True,
    ) -> "VaikoraTool":
        """
        Create a VaikoraTool from a plain function.

        Args:
            fn: Python function to wrap
            name: Tool name for LangChain
            description: Tool description for LangChain
            client: Vaikora SDK client instance
            action_type: Action type for policy evaluation
            resource: Resource identifier
            require_approval: Whether to require human approval
            check_pii: Whether to scan for PII in inputs
        """
        return cls(
            name=name,
            description=f"[Vaikora Protected] {description}",
            vaikora_client=client,
            vaikora_action_type=action_type,
            vaikora_resource=resource,
            vaikora_require_approval=require_approval,
            vaikora_check_pii=check_pii,
            _wrapped_fn=fn,
        )


def vaikora_tool(
    client: Any,
    action_type: str = "tool.execute",
    resource: Optional[str] = None,
    require_approval: bool = False,
    check_pii: bool = True,
):
    """
    Decorator to create a Vaikora-protected LangChain tool from a function.

    Usage:
        @vaikora_tool(client=vaikora_client, action_type="database.delete")
        def delete_user(user_id: str) -> str:
            '''Delete a user from the database.'''
            db.execute(f"DELETE FROM users WHERE id = {user_id}")
            return f"User {user_id} deleted"

    The decorated function becomes a LangChain-compatible tool that is
    automatically protected by Vaikora's security policies.
    """
    def decorator(fn: Callable) -> VaikoraTool:
        tool_name = fn.__name__
        tool_description = fn.__doc__ or f"Execute {tool_name}"

        return VaikoraTool.from_function(
            fn=fn,
            name=tool_name,
            description=tool_description,
            client=client,
            action_type=action_type,
            resource=resource,
            require_approval=require_approval,
            check_pii=check_pii,
        )

    return decorator
