"""API logging — minimal summary always; full headers/payload/response when logs=True."""
from __future__ import annotations

import json
from typing import Any, Optional
from urllib.parse import urlparse

_API_VERSION_PREFIXES = frozenset({"v1", "v2", "v3", "v4", "v5"})

# Cap only to protect log files from multi‑MB bodies; still "full" for normal APIs.
_VERBOSE_BODY_LIMIT = 200_000


def is_verbose_api_logging_enabled() -> bool:
    """True when ``[System] logs=True`` in config.ini."""
    try:
        from ws_package.configs import config

        return bool(config("logs", "System", dtype="bool"))
    except Exception:
        return False


def api_name_from_url(url: Optional[str]) -> str:
    """Derive a short API label from a URL path (e.g. .../data/key -> data/key)."""
    if not url or not str(url).strip():
        return "unknown"
    try:
        path = urlparse(str(url).strip()).path.strip("/")
        if not path:
            return "unknown"
        parts = [
            part
            for part in path.split("/")
            if part and part.lower() != "api" and part.lower() not in _API_VERSION_PREFIXES
        ]
        if not parts:
            return "unknown"
        return "/".join(parts[-2:]) if len(parts) >= 2 else parts[-1]
    except Exception:
        return "unknown"


def extract_api_code(http_status: int | str, body: Any = None) -> str:
    """Return API `code` from JSON body when present, else HTTP status."""
    api_code = str(http_status)
    parsed = body
    if isinstance(body, str) and body.strip():
        try:
            parsed = json.loads(body)
        except (json.JSONDecodeError, TypeError):
            return api_code
    if not parsed or not isinstance(parsed, dict):
        return api_code
    if parsed.get("code") is not None:
        api_code = str(parsed["code"])
    nested = parsed.get("response")
    if isinstance(nested, dict) and nested.get("code") is not None:
        api_code = str(nested["code"])
    return api_code


def log_api_response_summary(
    logger,
    url_or_name: str,
    http_status: int | str,
    body: Any = None,
    *,
    name: Optional[str] = None,
) -> None:
    """Log ``[API] {name} | HTTP {status} | code={code}`` — never the full URL.

    When ``logs=True``, also dumps the full response body.
    """
    api_name = name or api_name_from_url(url_or_name)
    if api_name == "unknown" and url_or_name and "/" not in str(url_or_name):
        api_name = str(url_or_name)
    api_code = extract_api_code(http_status, body)
    status_num = int(http_status) if str(http_status).isdigit() else 0
    line = f"[API] {api_name} | HTTP {http_status} | code={api_code}"
    if status_num >= 400 or api_code not in ("200", "201", "204"):
        logger.warning(line)
    else:
        logger.info(line)

    if is_verbose_api_logging_enabled():
        log_testing_response(logger, url_or_name, http_status, body, name=api_name)


_SENSITIVE_HEADER_KEYS = frozenset(
    {"authorization", "sdtoken", "sd_token", "token", "password", "desktopkey"}
)


def safe_headers_for_testing(headers: Any) -> dict:
    """Return a copy of request headers with auth tokens redacted for verbose logs."""
    if not headers:
        return {}
    safe: dict = {}
    items = headers.items() if isinstance(headers, dict) else []
    for key, value in items:
        text = str(value)
        if str(key).lower() in _SENSITIVE_HEADER_KEYS:
            suffix = text[-8:] if len(text) > 8 else ""
            if str(key).lower() == "authorization":
                safe[str(key)] = f"Bearer ***{suffix}" if suffix else "Bearer ***"
            else:
                safe[str(key)] = f"***{suffix}" if suffix else "***"
        else:
            safe[str(key)] = text
    return safe


def _format_body_for_logging(body: Any, limit: int = _VERBOSE_BODY_LIMIT) -> Optional[str]:
    if body is None:
        return None
    try:
        if isinstance(body, (dict, list)):
            text = json.dumps(body, default=str)
        else:
            text = str(body)
    except (TypeError, ValueError):
        text = str(body)
    text = text.strip()
    if not text:
        return None
    if len(text) > limit:
        return f"{text[:limit]}... [truncated {len(text) - limit} chars]"
    return text


def log_testing_request_headers(
    logger,
    method: str,
    url: Optional[str],
    headers: Any,
    body: Any = None,
) -> None:
    """When ``logs=True``, log full request headers and payload (tokens redacted)."""
    if not is_verbose_api_logging_enabled():
        return
    api_name = api_name_from_url(url)
    logger.info(
        "[API-HIT] REQUEST %s %s | headers: %s",
        method.upper(),
        api_name,
        safe_headers_for_testing(headers),
    )
    preview = _format_body_for_logging(body)
    if preview is not None:
        logger.info("[API-HIT] REQUEST %s | payload: %s", api_name, preview)


def log_testing_response(
    logger,
    url_or_name: Optional[str],
    http_status: int | str,
    body: Any = None,
    *,
    name: Optional[str] = None,
) -> None:
    """When ``logs=True``, log full response body."""
    if not is_verbose_api_logging_enabled():
        return
    api_name = name or api_name_from_url(url_or_name)
    preview = _format_body_for_logging(body)
    logger.info(
        "[API-HIT] RESPONSE %s | HTTP %s | body: %s",
        api_name,
        http_status,
        preview if preview is not None else "<empty>",
    )
