"""
Context injection module — builds AgentDeps for pydantic-ai tool functions.

With pydantic-ai, per-tool context injection (set_context, set_chat_context)
is replaced by a single AgentDeps object passed via RunContext to every tool.
This module constructs that object from the chat configuration.
"""

import asyncio
from typing import Optional, Any

from suzent.core.agent_deps import AgentDeps
from suzent.config import CONFIG, get_effective_volumes
from suzent.logger import get_logger

logger = get_logger(__name__)


def _get_config_value(config: Optional[dict], key: str, default: Any) -> Any:
    """Get a config value with fallback to default if config is None or key missing."""
    if config is None:
        return default
    return config.get(key, default)


def build_agent_deps(
    chat_id: str,
    user_id: str = None,
    config: Optional[dict] = None,
) -> AgentDeps:
    """
    Build an AgentDeps instance from chat configuration.

    This replaces the old inject_chat_context() function. Instead of mutating
    tool instances, we build a single dependency object that pydantic-ai
    injects into all tool functions via RunContext[AgentDeps].

    Args:
        chat_id: The chat session identifier.
        user_id: The user identifier (defaults to CONFIG.user_id).
        config: Optional per-chat configuration dict.

    Returns:
        AgentDeps instance ready to be passed to agent.run().
    """
    if user_id is None:
        user_id = CONFIG.user_id

    sandbox_enabled = _get_config_value(
        config, "sandbox_enabled", CONFIG.sandbox_enabled
    )
    suppress_environment_context = _get_config_value(
        config, "suppress_environment_context", False
    )
    workspace_root = _get_config_value(config, "workspace_root", CONFIG.workspace_root)
    cwd = _get_config_value(config, "cwd", None)
    auto_approve_tools = _get_config_value(config, "auto_approve_tools", False)
    # Layer 1 (lowest): global config from CONFIG / permissions.yaml
    # Layer 2 (middle): per-chat session rules persisted in DB
    # Layer 3 (highest): runtime config sent by client this request
    _global_perm = dict(CONFIG.permission_policies or {})
    _request_perm = _get_config_value(config, "permission_policies", None)

    _chat_obj = None
    try:
        from suzent.database import get_database as _gdb

        _chat_obj = _gdb().get_chat(chat_id)
        _chat_perm = (
            (_chat_obj.config or {}).get("permission_policies") if _chat_obj else None
        )
    except Exception:
        _chat_perm = None

    # Merge layers: later layers win on conflict; command_rules are unioned by pattern
    def _merge_perm(base: dict, overlay: dict) -> dict:
        result = dict(base)
        for tool, policy in overlay.items():
            if (
                tool in result
                and isinstance(result[tool], dict)
                and isinstance(policy, dict)
            ):
                merged_tool = {**result[tool], **policy}
                base_rules = {
                    r["pattern"]: r
                    for r in (result[tool].get("command_rules") or [])
                    if isinstance(r, dict) and r.get("pattern")
                }
                for r in policy.get("command_rules") or []:
                    if isinstance(r, dict) and r.get("pattern"):
                        base_rules[r["pattern"]] = r
                merged_tool["command_rules"] = list(base_rules.values())
                result[tool] = merged_tool
            else:
                result[tool] = policy
        return result

    tool_permission_policies = _global_perm
    if isinstance(_chat_perm, dict):
        tool_permission_policies = _merge_perm(tool_permission_policies, _chat_perm)
    if isinstance(_request_perm, dict):
        tool_permission_policies = _merge_perm(tool_permission_policies, _request_perm)
    custom_volumes = get_effective_volumes(
        _get_config_value(config, "sandbox_volumes", None)
    )
    try:
        from suzent.database import get_database

        custom_volume_metadata = get_database().get_volume_metadata(custom_volumes)
    except Exception as exc:
        logger.debug(f"Failed to load custom volume metadata: {exc}")
        custom_volume_metadata = {}

    # Build PathResolver
    from suzent.tools.filesystem.path_resolver import PathResolver

    path_resolver = PathResolver(
        chat_id,
        sandbox_enabled,
        sandbox_data_path=CONFIG.sandbox_data_path,
        custom_volumes=custom_volumes,
        workspace_root=workspace_root,
    )

    # Memory manager — skip if explicitly disabled in config
    from suzent.memory.lifecycle import get_memory_manager

    memory_enabled = _get_config_value(config, "memory_enabled", True)
    memory_manager = get_memory_manager() if memory_enabled else None

    # Social context
    runtime = _get_config_value(config, "_runtime", {})
    social_ctx = _get_config_value(config, "social_context", {})
    channel_manager = runtime.get("channel_manager") if runtime else None
    event_loop = runtime.get("event_loop") if runtime else None

    # Desktop mode fallback for social
    if not channel_manager:
        try:
            from suzent.core.social_brain import get_active_social_brain

            brain = get_active_social_brain()
            if brain:
                channel_manager = brain.channel_manager
                try:
                    event_loop = asyncio.get_running_loop()
                except RuntimeError:
                    event_loop = None
        except Exception:
            pass

    # Skill manager
    from suzent.skills import get_skill_manager

    skill_manager = get_skill_manager()

    # Merge tool_approval_policy across layers so a remembered decision survives
    # turns even if the client doesn't re-send it in the request config:
    #   Layer 1 (lowest): per-chat policy persisted in DB
    #   Layer 2 (highest): runtime config sent by client this request
    _request_approval_policy = _get_config_value(config, "tool_approval_policy", {})
    assert isinstance(_request_approval_policy, dict), (
        "tool_approval_policy must be a dict"
    )

    # Reuse the chat object fetched above for permission_policies.
    _chat_approval_policy = (
        (_chat_obj.config or {}).get("tool_approval_policy") if _chat_obj else None
    )

    tool_approval_policy: dict = {}
    if isinstance(_chat_approval_policy, dict):
        tool_approval_policy.update(_chat_approval_policy)
    # Runtime request wins on conflict.
    tool_approval_policy.update(_request_approval_policy)
    # SECURITY: defensive copy so each AgentDeps has its own independent dict.
    tool_approval_policy = dict(tool_approval_policy)
    permission_mode = _get_config_value(config, "permission_mode", None)
    if permission_mode is None and _chat_obj is not None:
        permission_mode = (_chat_obj.config or {}).get("permission_mode")
    permission_mode = str(permission_mode or "default")
    interaction_profile = str(
        _get_config_value(config, "interaction_profile", "interactive")
    )
    global_rules = list(getattr(CONFIG, "permission_rules", []) or [])
    chat_rules = (
        list((_chat_obj.config or {}).get("permission_rules") or [])
        if _chat_obj is not None
        else []
    )
    request_rules = list(_get_config_value(config, "permission_rules", []) or [])
    permission_rules = [*global_rules, *chat_rules, *request_rules]

    from suzent.core.file_tracker import FileTracker

    file_tracker = FileTracker(chat_id)

    base_tool_names = frozenset(_get_config_value(config, "tools", []) or [])

    return AgentDeps(
        chat_id=chat_id,
        user_id=user_id,
        suppress_environment_context=suppress_environment_context,
        sandbox_enabled=sandbox_enabled,
        workspace_root=workspace_root,
        cwd=cwd,
        custom_volumes=custom_volumes,
        custom_volume_metadata=custom_volume_metadata,
        path_resolver=path_resolver,
        memory_manager=memory_manager,
        channel_manager=channel_manager,
        event_loop=event_loop,
        social_context=social_ctx,
        skill_manager=skill_manager,
        auto_approve_tools=auto_approve_tools,
        tool_permission_policies=dict(tool_permission_policies or {}),
        tool_approval_policy=tool_approval_policy,
        permission_mode=permission_mode,
        interaction_profile=interaction_profile,
        permission_rules=permission_rules,
        base_tool_names=base_tool_names,
        a2ui_queue=asyncio.Queue(),
        inline_a2ui_surfaces={},
        file_tracker=file_tracker,
    )
