from __future__ import annotations

import json
from typing import Any


def parse_request_body(request: Any) -> bytes:
    if isinstance(request, dict):
        raw = request.get("body")
    else:
        raw = None
        for attr in ("body", "content", "data"):
            candidate = getattr(request, attr, None)
            if candidate is not None and candidate != b"":
                raw = candidate
                break

    return _to_bytes(raw)


def _to_bytes(raw: Any) -> bytes:
    if raw is None:
        return b""
    if isinstance(raw, (bytes, bytearray)):
        return _decode_chunked(bytes(raw))
    if isinstance(raw, str):
        return raw.encode("utf-8")
    if callable(getattr(raw, "read", None)):
        try:
            if hasattr(raw, "seek"):
                try:
                    raw.seek(0)
                except Exception:
                    pass
            data = raw.read()
            return _decode_chunked(data if isinstance(data, bytes) else str(data).encode())
        except Exception:
            return b""
    if hasattr(raw, "__iter__"):
        try:
            chunks = []
            for chunk in raw:
                chunks.append(chunk if isinstance(chunk, (bytes, bytearray)) else str(chunk).encode())
            return _decode_chunked(b"".join(chunks))
        except Exception:
            return b""
    return b""


def _decode_chunked(data: bytes) -> bytes:
    if not data:
        return data
    first_crlf = data.find(b"\r\n")
    if first_crlf <= 0 or first_crlf > 16:
        return data
    try:
        int(data[:first_crlf], 16)
    except ValueError:
        return data
    out, pos = bytearray(), 0
    while pos < len(data):
        crlf = data.find(b"\r\n", pos)
        if crlf < 0:
            break
        try:
            size = int(data[pos:crlf], 16)
        except ValueError:
            break
        if size == 0:
            break
        start = crlf + 2
        out.extend(data[start: start + size])
        pos = start + size + 2
    return bytes(out) if out else data


def decode_json(raw: bytes) -> dict:
    try:
        return json.loads(raw) if raw else {}
    except Exception:
        return {}


def parse_json_body(request: Any) -> dict:
    return decode_json(parse_request_body(request))


def encode_value_bytes(value: Any) -> bytes:
    """Convert a simple value to bytes (for Redis-like key-value stores)."""
    if isinstance(value, bytes):
        return value
    if value is None:
        return b""
    return str(value).encode()


def encode_response(body: Any) -> bytes:
    if body is None:
        return b""
    if isinstance(body, bytes):
        return body
    if isinstance(body, (dict, list)):
        return json.dumps(body, default=str).encode("utf-8")
    return str(body).encode("utf-8")


def is_generic_fallback(body: Any) -> bool:
    return isinstance(body, dict) and body.get("source") == "generic-fallback"


_AWS_HOSTS = (
    ".amazonaws.com",
    ".aws.amazon.com",
)

_AZURE_HOSTS = (
    ".blob.core.windows.net",
    ".cosmos.azure.com",
    ".documents.azure.com",
    ".database.windows.net",
    ".redis.cache.windows.net",
    ".servicebus.windows.net",
    ".vault.azure.net",
    ".queue.core.windows.net",
    ".table.core.windows.net",
    ".table.cosmos.azure.com",
    ".azconfig.io",
    ".azmk8s.io",
    ".azurecontainerapps.io",
    "management.azure.com",
    "management.core.windows.net",
    "login.microsoftonline.com",
    "login.windows.net",
    "login.live.com",
    "sts.windows.net",
    "169.254.169.254",
)

_GCP_HOSTS = (
    ".googleapis.com",
    ".google.com",
    "metadata.google.internal",
    "accounts.google.com",
    "oauth2.googleapis.com",
)


def cloud_provider_for_url(url: str) -> str:
    url_lower = url.lower()
    if any(h in url_lower for h in _AZURE_HOSTS):
        return "azure"
    if any(h in url_lower for h in _GCP_HOSTS):
        return "gcp"
    if any(h in url_lower for h in _AWS_HOSTS):
        return "aws"
    return ""
