from __future__ import annotations

import logging
from datetime import datetime, timedelta, timezone
from http import HTTPStatus

import httpx

from .exceptions import LoyaAuthError
from .utils import extract_expiration_from_session

logger = logging.getLogger(__name__)


class LoyaAuthenticator:
    """Отвечает только за авторизацию и управление сессией"""

    def __init__(
        self,
        client: httpx.AsyncClient,
        email: str,
        password_hash: str,
        apikey: str,
    ):
        self.client = client
        self.email = email
        self.password_hash = password_hash
        self.apikey = apikey

        self._session_cookie: str | None = None
        self._expires_at: datetime | None = None

    @property
    def is_authenticated(self) -> bool:
        return self._session_cookie is not None

    @property
    def expires_at(self) -> datetime | None:
        return self._expires_at

    async def login(self) -> None:
        logger.info("Loya: выполняется авторизация...")
        payload = {
            "email": self.email,
            "password": self.password_hash,
            "apikey": self.apikey,
        }

        response = await self.client.post(
            "/api/1.0/login",
            json=payload,
            headers={"Content-Type": "application/json"},
        )

        if response.status_code != HTTPStatus.OK or response.text.strip() != "login success":
            raise LoyaAuthError(f"Авторизация не удалась: {response.status_code} {response.text}")

        cookie = response.headers.get("Set-Cookie", "")
        session_cookie = None
        for part in cookie.split(";"):
            if part.strip().startswith("PLAY2AUTH_SESS_ID="):
                session_cookie = part.strip().split("=", 1)[1]
                break

        if not session_cookie:
            raise LoyaAuthError("PLAY2AUTH_SESS_ID cookie not received")

        self._session_cookie = session_cookie
        self.client.headers["Cookie"] = f"PLAY2AUTH_SESS_ID={session_cookie}"

        expires_at = extract_expiration_from_session(session_cookie)
        self._expires_at = expires_at or (datetime.now(timezone.utc) + timedelta(hours=1))
        logger.info("Авторизация успешна. Сессия до: %s", self._expires_at.isoformat())

    async def ensure_auth(self, refresh_margin: timedelta = timedelta(minutes=5)) -> None:
        now = datetime.now(timezone.utc)
        if (
            not self.is_authenticated
            or not self._expires_at
            or now >= self._expires_at - refresh_margin
        ):
            await self.login()

    def update_from_set_cookie(self, set_cookie_header: str) -> bool:
        """Обновляет куку, если сервер прислал новую"""
        if "PLAY2AUTH_SESS_ID=" not in set_cookie_header:
            return False

        for raw_part in set_cookie_header.split(";"):
            part = raw_part.strip()
            if part.startswith("PLAY2AUTH_SESS_ID="):
                new_cookie = part.split("=", 1)[1]
                if new_cookie != self._session_cookie:
                    logger.debug("Сессия обновлена через Set-Cookie")
                    self._session_cookie = new_cookie
                    self.client.headers["Cookie"] = f"PLAY2AUTH_SESS_ID={new_cookie}"
                    self._expires_at = extract_expiration_from_session(new_cookie)
                return True
        return False

    def clear(self) -> None:
        self._session_cookie = None
        self._expires_at = None
        self.client.headers.pop("Cookie", None)
