from __future__ import annotations

import json
from typing import Any, Generator, Iterator, Optional


class FakeHTTPBody:
    def __init__(self, body: bytes, status: int = 200) -> None:
        self._data       = body
        self.status      = status
        self.status_code = status
        self.headers: dict = {}

    def read(self, amt: Optional[int] = None) -> bytes:
        if amt is None:
            result, self._data = self._data, b""
        else:
            result, self._data = self._data[:amt], self._data[amt:]
        return result

    def stream(self, chunk_size: int = 1024, **_: Any) -> Generator[bytes, None, None]:
        yield self._data
        self._data = b""


def make_aws_response(body: dict, status: int = 200) -> Any:
    encoded = json.dumps(body, default=str).encode()
    raw     = FakeHTTPBody(encoded, status)
    try:
        import botocore.awsrequest  # type: ignore
        aws_resp = botocore.awsrequest.AWSResponse(url="", status_code=status, headers={}, raw=raw)
    except Exception:
        aws_resp = raw  # type: ignore[assignment]
    return aws_resp, add_metadata(body, status)


def add_metadata(body: dict, status: int = 200) -> dict:
    result = dict(body)
    result.setdefault("ResponseMetadata", {
        "RequestId":      "mock-request-id",
        "HTTPStatusCode": status,
        "HTTPHeaders":    {},
        "RetryAttempts":  0,
    })
    return result


class FakeAzureResponse:
    class _FakeRequest:
        def __init__(self, method: str = "PUT") -> None:
            self.method  = method
            self.url     = "https://management.azure.com/mock"
            self.headers: dict = {}
            self.body    = None

    def __init__(self, status: int, headers: dict, body: bytes,
                 method: str = "PUT") -> None:
        self.status_code      = status
        self.headers          = headers
        self._body            = body
        self.content_type     = headers.get("Content-Type", "application/json")
        self.internal_request = None

        self.request           = self._FakeRequest(method)
        self.http_response     = self
        self.context: dict     = {}
        self.content           = body


        self.is_closed           = True
        self.is_stream_consumed  = True

    def body(self) -> bytes:
        return self._body

    def read(self) -> bytes:
        return self._body

    def text(self, encoding: Optional[str] = None) -> str:
        return self._body.decode(encoding or "utf-8")

    def json(self) -> Any:
        return json.loads(self._body or b"{}")

    def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]:
        if self._body:
            yield self._body

    def iter_raw(self, chunk_size: Optional[int] = None) -> Iterator[bytes]:
        if self._body:
            yield self._body

    def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]:
        if self._body:
            yield self._body.decode("utf-8", errors="replace")

    def stream_download(self, pipeline: Any = None, **kwargs: Any) -> Iterator[bytes]:
        if self._body:
            yield self._body

    def raise_for_status(self) -> None:
        if self.status_code >= 400:
            raise Exception(f"Azure mock HTTP {self.status_code}")

    def close(self) -> None:
        pass
