"""aiohttp replay for AutoDebug.

This module provides replay of captured aiohttp HTTP requests during test replay.
It uses the same HTTP transcript as requests/httpx replay.
"""

from __future__ import annotations

import base64
import json
from typing import Any, Callable, Dict, Optional, TYPE_CHECKING

from autodebug_replay.canonicalize import canonical_url

if TYPE_CHECKING:
    from autodebug_replay.transcripts import TranscriptIndex

# Original functions
_original_request: Optional[Callable] = None

# Global transcript index
_index: Optional["TranscriptIndex"] = None


def _decode_body(body_bytes: Any, encoding: str) -> bytes:
    """Decode recorded body back to bytes."""
    if body_bytes is None:
        return b""
    if encoding == "base64":
        return base64.b64decode(body_bytes)
    if isinstance(body_bytes, str):
        return body_bytes.encode("utf-8")
    return body_bytes


class _FakeResponse:
    """Fake aiohttp response for replay."""

    def __init__(self, status: int, headers: Dict[str, str], body: bytes, url: str):
        self.status = status
        self._headers = headers
        self._body = body
        self._url = url

    @property
    def headers(self):
        """Return headers as a dict-like object."""
        return self._headers

    @property
    def url(self):
        """Return request URL."""
        return self._url

    def get_encoding(self) -> str:
        """Get response encoding."""
        content_type = self._headers.get("content-type", "")
        if "charset=" in content_type:
            return content_type.split("charset=")[-1].split(";")[0].strip()
        return "utf-8"

    async def read(self) -> bytes:
        """Return response body."""
        return self._body

    async def text(self, encoding: Optional[str] = None) -> str:
        """Return body as text."""
        enc = encoding or self.get_encoding()
        return self._body.decode(enc, errors="replace")

    async def json(self, **kwargs) -> Any:
        """Return body as JSON."""
        return json.loads(self._body)

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        pass


async def _patched_request(self, method: str, url, **kwargs):
    """Patched ClientSession._request that replays from transcript."""
    from autodebug_replay.transcripts import ReplayDivergence

    canonical = canonical_url(str(url))

    # Get next HTTP record from transcript
    record = _index.expect("HTTP")

    # Verify URL matches
    if record["url_canonical"] != canonical:
        raise ReplayDivergence("HTTP_URL_MISMATCH", {
            "expected": record["url_canonical"],
            "actual": canonical,
        })

    # Verify method matches
    if record["method"] != method.upper():
        raise ReplayDivergence("HTTP_METHOD_MISMATCH", {
            "expected": record["method"],
            "actual": method.upper(),
            "url": canonical,
        })

    # Decode response body
    response_body = _decode_body(
        record.get("response_body_bytes"),
        record.get("response_body_encoding", "utf-8")
    )

    # Build fake response
    return _FakeResponse(
        status=record["status"],
        headers=record.get("headers_raw", {}),
        body=response_body,
        url=str(url),
    )


def install(index: "TranscriptIndex") -> None:
    """Install aiohttp replay patches.

    Args:
        index: TranscriptIndex containing recorded HTTP requests
    """
    global _original_request, _index

    try:
        import aiohttp
    except ImportError:
        return

    if _original_request is not None:
        return  # Already patched

    _index = index
    _original_request = aiohttp.ClientSession._request
    aiohttp.ClientSession._request = _patched_request


def uninstall() -> None:
    """Restore original aiohttp functions."""
    global _original_request, _index

    try:
        import aiohttp
    except ImportError:
        return

    if _original_request is not None:
        aiohttp.ClientSession._request = _original_request
        _original_request = None

    _index = None
