import json
from typing import Any, Optional, Iterable

from cuid2 import cuid_wrapper

from frisk_sdk.core.wrap_tool_options import WrapToolOptions
from frisk_sdk.framework_adapter.framework_adapter import (
    FrameworkAdapter,
    NormalizedToolCall,
)
from frisk_sdk.redact import (
    RedactedDictionary,
    RedactedSerializedObject,
    RedactOption,
    redact_dictionary,
)


# todo: Wrap base adapter in try-catch so that if someone imports the wrong one, they get a helpful message: "Did you forget to use an adapter"? https://linear.app/friskai/issue/POL-97/small-cleanup-task-tracker
class BaseFrameworkAdapter(FrameworkAdapter[Any, Any]):
    """Default adapter for tool-like objects exposing ``name`` and ``args`` fields."""

    def __init__(self):
        self._cuid_generator = cuid_wrapper()

    def serialize_tool_args(
        self,
        tool_args: Optional[dict[str, Any]],
        *,
        redact: Optional[RedactOption] = None,
    ) -> RedactedSerializedObject:
        if tool_args is None:
            return RedactedSerializedObject(value=None, redacted_paths=[])
        tool_args_as_dict = self.tool_args_to_dict(tool_args, redact=redact)
        return RedactedSerializedObject(
            value=json.dumps(tool_args_as_dict.value),
            redacted_paths=tool_args_as_dict.redacted_paths,
        )

    def serialize_agent_state(
        self,
        agent_state: Optional[dict[str, Any]],
        *,
        redact: Optional[RedactOption] = None,
    ) -> RedactedSerializedObject:
        if agent_state is None:
            return RedactedSerializedObject(value=None, redacted_paths=[])
        agent_state_as_dict = self.agent_state_to_dict(agent_state, redact=redact)
        return RedactedSerializedObject(
            value=json.dumps(agent_state_as_dict.value),
            redacted_paths=agent_state_as_dict.redacted_paths,
        )

    def normalize_tool_call(self, tool_call: Any) -> NormalizedToolCall:
        return NormalizedToolCall(
            id=self._cuid_generator(), name=tool_call.name, args=tool_call.args
        )

    def tool_args_to_dict(
        self, tool_args: dict[str, Any], *, redact: Optional[RedactOption] = None
    ) -> RedactedDictionary:
        return redact_dictionary(tool_args, redact)

    def agent_state_to_dict(
        self, agent_state: dict[str, Any], *, redact: Optional[RedactOption] = None
    ) -> RedactedDictionary:
        return redact_dictionary(agent_state, redact)

    def extract_prompt(self, agent_state: dict[str, Any]) -> str | None:
        return agent_state.get("prompt")

    def wrap_tools(
        self, tools: Iterable[Any], options: WrapToolOptions | None = None
    ) -> list[Any]:
        return list(tools)

    def wrap_tool(self, tool: Any, options: WrapToolOptions | None = None) -> Any:
        return tool
