"""Session management for the Flowlines backend."""

from __future__ import annotations

import asyncio
import json
import logging
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

logger = logging.getLogger("flowlines")


class FlowlinesSessionError(Exception):
    """Raised when a session API call to the Flowlines backend fails.

    Attributes:
        status_code: The HTTP status code, or ``None`` for network/parse errors.
    """

    def __init__(self, message: str, *, status_code: int | None = None) -> None:
        super().__init__(message)
        self.status_code = status_code


def end_session(
    *,
    api_key: str,
    api_endpoint: str,
    user_id: str,
    session_id: str,
) -> None:
    """Mark a session as ended on the Flowlines backend (synchronous).

    Args:
        api_key: The Flowlines API key.
        api_endpoint: The base API endpoint.
        user_id: The user ID (required).
        session_id: The session ID (required).

    Raises:
        FlowlinesSessionError: On HTTP errors (other than 404), network
            failures, or malformed responses.
    """
    base = api_endpoint.rstrip("/")
    url = f"{base}/v1/mark-session-as-ended"
    body = json.dumps({"session_id": session_id, "user_id": user_id}).encode()
    request = Request(  # noqa: S310
        url,
        data=body,
        headers={
            "x-flowlines-api-key": api_key,
            "Content-Type": "application/json",
            "User-Agent": "flowlines-sdk-py",
        },
        method="POST",
    )

    try:
        urlopen(request, timeout=30)  # noqa: S310
    except HTTPError as exc:
        if exc.code == 404:
            logger.debug("end_session returned HTTP 404 — ignoring")
            return
        resp_body = exc.read().decode("utf-8", errors="replace")
        try:
            detail = json.loads(resp_body).get("error", resp_body)
        except (json.JSONDecodeError, AttributeError):
            detail = resp_body
        msg = f"end_session failed (HTTP {exc.code}): {detail}"
        raise FlowlinesSessionError(msg, status_code=exc.code) from exc
    except URLError as exc:
        msg = f"end_session failed: {exc.reason}"
        raise FlowlinesSessionError(msg) from exc


async def aend_session(
    *,
    api_key: str,
    api_endpoint: str,
    user_id: str,
    session_id: str,
) -> None:
    """Mark a session as ended on the Flowlines backend (asynchronous).

    Delegates to :func:`end_session` via :func:`asyncio.to_thread`.
    See :func:`end_session` for full parameter documentation.

    Raises:
        FlowlinesSessionError: On HTTP errors, network failures, or
            malformed responses.
    """
    await asyncio.to_thread(
        end_session,
        api_key=api_key,
        api_endpoint=api_endpoint,
        user_id=user_id,
        session_id=session_id,
    )
