"""
ApiKeyRotationHelper — zero-downtime API key rotation.

Usage:
    helper = ApiKeyRotationHelper(client, iam_base_url="...", admin_jwt=os.environ["IAM_ADMIN_JWT"])
    created = helper.create_new_key(name="HR Cron v2")
    # deploy created["key_value"] to service, then:
    helper.revoke_key(old_key_id, grace_period_ms=5 * 60 * 1000)
"""
from __future__ import annotations

import time
from typing import Any

import requests

from .client import IamClient, IamError
from .types import ApiKeyCreatedData, ApiKeyData, IamApiResponse


class ApiKeyRotationHelper:
    def __init__(
        self,
        client: IamClient,
        *,
        iam_base_url: str,
        admin_jwt: str,
        timeout_ms: int = 10_000,
    ):
        self._client = client
        self._iam_base_url = iam_base_url.rstrip("/")
        self._admin_jwt = admin_jwt
        self._timeout_s = timeout_ms / 1000.0

    def _session(self) -> requests.Session:
        s = requests.Session()
        s.headers["Content-Type"] = "application/json"
        s.headers["Authorization"] = f"Bearer {self._admin_jwt}"
        return s

    def create_new_key(
        self,
        *,
        name: str,
        description: str | None = None,
        owner_id: int | None = None,
        scopes: list[str] | None = None,
        expires_at: str | None = None,
    ) -> ApiKeyCreatedData:
        body: dict[str, Any] = {"name": name}
        if description is not None:
            body["description"] = description
        if owner_id is not None:
            body["owner_id"] = owner_id
        if scopes is not None:
            body["scopes"] = scopes
        if expires_at is not None:
            body["expires_at"] = expires_at

        with self._session() as session:
            try:
                resp = session.post(
                    f"{self._iam_base_url}/api/internal/apikeys",
                    json=body,
                    timeout=self._timeout_s,
                )
                resp.raise_for_status()
            except requests.RequestException as exc:
                raise IamError(f"ApiKeyRotationHelper: createNewKey failed: {exc}") from exc
        data: IamApiResponse = resp.json()
        if not data.get("success") or not data.get("data"):
            raise IamError(
                f"ApiKeyRotationHelper: createNewKey failed — {data.get('message') or data.get('code')}"
            )
        return data["data"]

    def revoke_key(
        self,
        key_id: str,
        *,
        grace_period_ms: int = 0,
        reason: str | None = None,
    ) -> None:
        if grace_period_ms > 0:
            time.sleep(grace_period_ms / 1000.0)

        with self._session() as session:
            try:
                resp = session.post(
                    f"{self._iam_base_url}/api/internal/apikeys/{key_id}/revoke",
                    json={"reason": reason} if reason else {},
                    timeout=self._timeout_s,
                )
                resp.raise_for_status()
            except requests.RequestException as exc:
                raise IamError(f"ApiKeyRotationHelper: revokeKey failed: {exc}") from exc
        data = resp.json()
        if not data.get("success"):
            raise IamError(
                f"ApiKeyRotationHelper: revokeKey failed — {data.get('message') or data.get('code')}"
            )
        self._client.clear_api_key_cache()

    def list_active_keys(self, owner_id: int | None = None) -> list[ApiKeyData]:
        params: dict[str, str] = {"status": "active"}
        if owner_id is not None:
            params["owner_id"] = str(owner_id)
        with self._session() as session:
            try:
                resp = session.get(
                    f"{self._iam_base_url}/api/internal/apikeys",
                    params=params,
                    timeout=self._timeout_s,
                )
                resp.raise_for_status()
            except requests.RequestException as exc:
                raise IamError(f"ApiKeyRotationHelper: listActiveKeys failed: {exc}") from exc
        data = resp.json()
        if not data.get("success") or not data.get("data"):
            raise IamError(
                f"ApiKeyRotationHelper: listActiveKeys failed — {data.get('message') or data.get('code')}"
            )
        return data["data"]["items"]
