"""
senzib.integrations.profitbase.auth

Чистые помощники для авторизации в Profitbase API v4.

Модуль:
- НЕ знает про БД, Flask, app.config и т.п.
- НЕ сохраняет токены.
- Только общается с Profitbase и возвращает dict с токенами и expires_at.

Как устроена авторизация:
    POST https://{pb_account_number}.profitbase.ru/api/v4/json/authentication
    body:
        {
            "type": "api-app",
            "credentials": {
                "pb_api_key": "<API_KEY>"
            }
        }

Ответ содержит access_token и remaining_time (секунды до истечения).
Мы считаем expires_at = now + remaining_time - skew.
"""

from __future__ import annotations

import logging
import time
from typing import Any, Dict, Mapping, Optional

import requests

logger = logging.getLogger(__name__)

JsonDict = Dict[str, Any]


class ProfitbaseAuthError(Exception):
    """Ошибка при авторизации в Profitbase."""


def authenticate(
    pb_account_number: str,
    pb_api_key: str,
    *,
    request_timeout: int = 30,
    skew: int = 100,
) -> JsonDict:
    """
    Получить новый access_token Profitbase по API-ключу.

    :param pb_account_number:
        Номер аккаунта Profitbase (поддомен без .profitbase.ru),
        например "pb15930".
    :param pb_api_key:
        API-ключ интеграции Profitbase ("pb_api_key").
    :param request_timeout:
        Таймаут HTTP-запроса (секунды).
    :param skew:
        Запас в секундах, на который мы "подрезаем" срок жизни токена,
        чтобы не упереться в точный момент истечения. По умолчанию 100 сек.
    :return:
        dict с данными авторизации, как возвращает Profitbase,
        + поле "expires_at" (unix timestamp).
        Пример:
            {
                "access_token": "...",
                "remaining_time": 86399,
                "expires_at": 1712345678,
                ...
            }
    :raises ProfitbaseAuthError:
        При сетевой ошибке или неуспешном статус-коде/формате ответа.
    """
    url = f"https://{pb_account_number}.profitbase.ru/api/v4/json/authentication"
    payload = {
        "type": "api-app",
        "credentials": {
            "pb_api_key": pb_api_key,
        },
    }

    logger.info("Requesting Profitbase access_token for account %s", pb_account_number)

    try:
        resp = requests.post(url, json=payload, timeout=request_timeout)
    except requests.RequestException as exc:
        raise ProfitbaseAuthError(
            f"Network error while authenticating with Profitbase: {exc}"
        ) from exc

    if resp.status_code != 200:
        raise ProfitbaseAuthError(
            f"Profitbase auth failed for {pb_account_number}: "
            f"HTTP {resp.status_code}, body={resp.text}"
        )

    data: JsonDict = resp.json() or {}

    access_token = data.get("access_token")
    if not access_token:
        raise ProfitbaseAuthError(
            f"Profitbase auth response has no access_token: {data}"
        )

    remaining_time = data.get("remaining_time", 86400)  # по умолчанию сутки
    try:
        rt = int(remaining_time)
    except (TypeError, ValueError):
        rt = 86400

    # Устанавливаем expires_at с учётом skew
    expires_at = int(time.time()) + max(rt - skew, 0)
    data["expires_at"] = expires_at

    return data


def is_token_expired(token_data: Mapping[str, Any], *, skew: int = 0) -> bool:
    """
    Проверяет, истёк ли токен Profitbase (или истечёт очень скоро).

    :param token_data:
        dict, сохранённый после authenticate(), ожидается поле expires_at.
        Если его нет, попробуем оценить по remaining_time.
    :param skew:
        Сколько секунд "запаса" считать — т.е. считать токен
        истёкшим на skew секунд раньше.
    :return:
        True, если токен нужно обновить (или данные некорректны),
        False, если токен ещё жив.
    """
    expires_at = token_data.get("expires_at")

    if expires_at is None:
        # fallback: оценим по remaining_time, если есть
        remaining_time = token_data.get("remaining_time")
        if remaining_time is None:
            # данных нет — считаем токен сомнительным, лучше обновить
            return True
        try:
            rt = int(remaining_time)
        except (TypeError, ValueError):
            return True

        # представляем, что этот remaining_time актуален "прямо сейчас"
        expires_at = int(time.time()) + rt

    try:
        exp_ts = int(expires_at)
    except (TypeError, ValueError):
        return True

    now = int(time.time())
    return now >= (exp_ts - int(skew))


__all__ = [
    "ProfitbaseAuthError",
    "authenticate",
    "is_token_expired",
]


"""
ПРИМЕР ИСПОЛЬЗОВАНИЯ В ПРИЛОЖЕНИИ

from senzib.integrations.profitbase.auth import authenticate, is_token_expired

def get_pb_access_token(platform, account_id, pb_account_number, pb_api_key):
    # 1) читаем oauth_json из БД (как раньше)
    row = load_profitbase_row_from_db(platform, account_id, pb_account_number)
    if row and row["oauth"]:
        token_data = json.loads(row["oauth"])
        if not is_token_expired(token_data, skew=60):
            return token_data["access_token"]

    # 2) если нет токена или истёк — берём новый и сохраняем
    token_data = authenticate(pb_account_number, pb_api_key)
    save_profitbase_oauth_to_db(platform, account_id, pb_account_number, pb_api_key, token_data)
    return token_data["access_token"]
"""