from __future__ import annotations

from dataclasses import dataclass, is_dataclass, asdict
from typing import Union, Optional, cast, TypedDict, Any

RedactOption = Union[bool, list[str]]


def normalize_redact_option(redact_option: Optional[RedactOption]) -> RedactOption:
    """Normalize optional redaction input into canonical internal representation."""
    if redact_option is None:
        return False

    if isinstance(redact_option, bool):
        return redact_option

    return redact_option


def combine_redact_options(
    a: Optional[RedactOption], b: Optional[RedactOption]
) -> RedactOption:
    """Combine two redaction options into a single effective option."""
    a = normalize_redact_option(a)
    b = normalize_redact_option(b)

    if is_redact_option_falsy(a):
        return b
    if is_redact_option_falsy(b):
        return a

    if a is True or b is True:
        return True

    return cast(list[str], a) + cast(list[str], b)


def is_redact_option_falsy(redact: RedactOption | None) -> bool:
    """Return ``True`` when a redaction option semantically means no redaction."""
    return redact in [None, False, []]


class RedactionOptionsDict(TypedDict, total=False):
    """Dictionary form of SDK redaction options."""

    redact_tool_args: Optional[RedactOption]
    redact_agent_state: Optional[RedactOption]


@dataclass
class RedactionOptions:
    """Dataclass form of SDK redaction options."""

    redact_tool_args: Optional[RedactOption] = None
    redact_agent_state: Optional[RedactOption] = None


RedactionOptionsInput = Union[
    RedactionOptionsDict, RedactionOptions, dict[str, Any], None
]


def normalize_redaction_options(
    opts: object | None = None,
) -> RedactionOptionsDict:
    """Normalize supported option input shapes into a stable redaction dictionary."""
    defaults: RedactionOptionsDict = {
        "redact_tool_args": None,
        "redact_agent_state": None,
    }

    if opts is None:
        return defaults
    if not isinstance(opts, type) and is_dataclass(opts):
        # asdict returns dict[str, Any]; cast to our stable output shape.
        return cast(RedactionOptionsDict, asdict(cast(Any, opts)))

    # Treat anything else as mapping-like; dict(...) will raise TypeError otherwise.
    opts_dict = dict(cast(dict[str, Any], opts))
    merged = {**defaults, **opts_dict}
    return cast(RedactionOptionsDict, merged)
