from __future__ import annotations

from typing import Any, Optional, Protocol


class ResponseExtractor(Protocol):
    def can_handle(self, payload: dict) -> bool:
        ...

    def extract_text(self, payload: dict) -> Optional[str]:
        ...

    def extract_delta(self, payload: dict) -> Optional[str]:
        ...


class ResponseAccessor:
    def as_list(self, value: Any) -> Optional[list]:
        return value if isinstance(value, list) else None

    def as_dict(self, value: Any) -> Optional[dict]:
        return value if isinstance(value, dict) else None

    def as_str(self, value: Any) -> Optional[str]:
        return value if isinstance(value, str) else None

    def first_item(self, value: Any) -> Optional[Any]:
        items = self.as_list(value)
        if items:
            return items[0]
        return None


class OutputFieldExtractor:
    def __init__(self, accessor: ResponseAccessor | None = None) -> None:
        self._accessor = accessor or ResponseAccessor()

    def can_handle(self, payload: dict) -> bool:
        return "output" in payload

    def extract_text(self, payload: dict) -> Optional[str]:
        output = payload.get("output")
        output_str = self._accessor.as_str(output)
        if output_str is not None:
            return output_str.strip()
        first_item = self._accessor.first_item(output)
        if first_item is not None:
            first_dict = self._accessor.as_dict(first_item)
            if first_dict is not None:
                content = first_dict.get("content") or first_dict.get("text")
                content_str = self._accessor.as_str(content)
                if content_str is not None:
                    return content_str.strip()
            return str(first_item).strip()
        output_dict = self._accessor.as_dict(output)
        if output_dict is not None:
            content = output_dict.get("content")
            content_str = self._accessor.as_str(content)
            if content_str is not None:
                return content_str.strip()
            delta = self._accessor.as_dict(output_dict.get("delta"))
            if delta is not None:
                delta_content = self._accessor.as_str(delta.get("content"))
                if delta_content is not None:
                    return delta_content.strip()
        if output is None:
            return None
        return str(output).strip()

    def extract_delta(self, payload: dict) -> Optional[str]:
        output = payload.get("output")
        output_str = self._accessor.as_str(output)
        if output_str is not None:
            return output_str
        first_item = self._accessor.first_item(output)
        if first_item is not None:
            first_dict = self._accessor.as_dict(first_item)
            if first_dict is not None:
                content = first_dict.get("content") or first_dict.get("text")
                content_str = self._accessor.as_str(content)
                if content_str is not None:
                    return content_str
                delta = self._accessor.as_dict(first_dict.get("delta"))
                if delta is not None:
                    delta_content = self._accessor.as_str(delta.get("content"))
                    if delta_content is not None:
                        return delta_content
            return None
        output_dict = self._accessor.as_dict(output)
        if output_dict is not None:
            delta = self._accessor.as_dict(output_dict.get("delta"))
            if delta is not None:
                delta_content = self._accessor.as_str(delta.get("content"))
                if delta_content is not None:
                    return delta_content
            content = self._accessor.as_str(output_dict.get("content"))
            if content is not None:
                return content
        return None


class ChoicesFieldExtractor:
    def __init__(self, accessor: ResponseAccessor | None = None) -> None:
        self._accessor = accessor or ResponseAccessor()

    def can_handle(self, payload: dict) -> bool:
        return bool(self._accessor.as_list(payload.get("choices")))

    def extract_text(self, payload: dict) -> Optional[str]:
        choices = self._accessor.as_list(payload.get("choices"))
        if not choices:
            return None
        first_choice = choices[0]
        first_dict = self._accessor.as_dict(first_choice)
        if first_dict is not None:
            message = self._accessor.as_dict(first_dict.get("message"))
            if message is not None:
                content = self._accessor.as_str(message.get("content"))
                if content is not None:
                    return content.strip()
            content = self._accessor.as_str(first_dict.get("content"))
            if content is not None:
                return content.strip()
        return None

    def extract_delta(self, payload: dict) -> Optional[str]:
        choices = self._accessor.as_list(payload.get("choices"))
        if not choices:
            return None
        first_choice = choices[0]
        first_dict = self._accessor.as_dict(first_choice)
        if first_dict is not None:
            delta = self._accessor.as_dict(first_dict.get("delta"))
            if delta is not None:
                content = self._accessor.as_str(delta.get("content"))
                if content is not None:
                    return content
        return None


class ContentFieldExtractor:
    def __init__(self, accessor: ResponseAccessor | None = None) -> None:
        self._accessor = accessor or ResponseAccessor()

    def can_handle(self, payload: dict) -> bool:
        return self._accessor.as_str(payload.get("content")) is not None

    def extract_text(self, payload: dict) -> Optional[str]:
        content = self._accessor.as_str(payload.get("content"))
        return content.strip() if content is not None else None

    def extract_delta(self, payload: dict) -> Optional[str]:
        return None


class DeltaFieldExtractor:
    def __init__(self, accessor: ResponseAccessor | None = None) -> None:
        self._accessor = accessor or ResponseAccessor()

    def can_handle(self, payload: dict) -> bool:
        return self._accessor.as_dict(payload.get("delta")) is not None

    def extract_text(self, payload: dict) -> Optional[str]:
        return None

    def extract_delta(self, payload: dict) -> Optional[str]:
        delta = self._accessor.as_dict(payload.get("delta"))
        if delta is None:
            return None
        return self._accessor.as_str(delta.get("content"))


def _default_extractors() -> list[ResponseExtractor]:
    return [
        OutputFieldExtractor(),
        ChoicesFieldExtractor(),
        ContentFieldExtractor(),
    ]


def _default_delta_extractors() -> list[ResponseExtractor]:
    return [
        OutputFieldExtractor(),
        ChoicesFieldExtractor(),
        DeltaFieldExtractor(),
    ]


def extract_text_from_response(payload: dict) -> str:
    if not isinstance(payload, dict):
        return ""
    for extractor in _default_extractors():
        if not extractor.can_handle(payload):
            continue
        text = extractor.extract_text(payload)
        if text is not None:
            return text
    return ""


def extract_stream_delta(payload: dict) -> Optional[str]:
    if not isinstance(payload, dict):
        return None
    for extractor in _default_delta_extractors():
        if not extractor.can_handle(payload):
            continue
        delta = extractor.extract_delta(payload)
        if delta is not None:
            return delta
    return None
