"""HTTP client helpers for QRM services."""

from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any, Dict, Optional
from urllib.parse import urljoin

import requests
import urllib3
from pydantic import BaseModel, Field, HttpUrl


class LoginCredentials(BaseModel):
    """Payload accepted by the `/User/Login` endpoint."""

    username: str = Field(..., min_length=1)
    password: str = Field(..., min_length=1)


class LoginState(BaseModel):
    """Serialized login state saved on disk by the CLI."""

    token: str = Field(..., min_length=1)
    username: str = Field(..., min_length=1)
    base_url: HttpUrl
    verify_tls: bool = True
    stored_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


class QrmClientError(RuntimeError):
    """Raised when the client cannot fulfill a request."""


@dataclass
class QrmClient:
    """Thin wrapper around the QRM HTTP API."""

    base_url: str
    verify_tls: bool = True
    timeout: float = 10.0
    session: Optional[requests.Session] = None

    def __post_init__(self) -> None:
        if not self.base_url:
            raise ValueError("base_url must be provided")
        self.base_url = self.base_url.rstrip("/")
        self.session = self.session or requests.Session()
        self.session.verify = self.verify_tls
        if not self.verify_tls:
            urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

    def login(self, username: str, password: str) -> str:
        """Authenticate against `/User/Login` and return the bearer token."""

        credentials = LoginCredentials(username=username, password=password)
        try:
            response = self.session.post(
                self._build_url("/User/Login"),
                json=credentials.model_dump(),
                timeout=self.timeout,
            )
            response.raise_for_status()
        except requests.RequestException as exc:  # pragma: no cover - network failure
            raise QrmClientError("Failed to contact QRM login endpoint") from exc

        token = self._extract_token(response)
        if not token:
            raise QrmClientError("QRM login response did not contain a token")
        return token

    def status(self, token: Optional[str] = None) -> Any:
        """Query the `/Result/Status` endpoint to verify service health."""

        headers = {"Authorization": f"Bearer {token}"} if token else None
        try:
            response = self.session.get(
                self._build_url("/Result/Status"),
                headers=headers,
                timeout=self.timeout,
            )
            response.raise_for_status()
        except requests.RequestException as exc:  # pragma: no cover - network failure
            raise QrmClientError("Failed to contact QRM status endpoint") from exc

        try:
            return response.json()
        except ValueError:
            return response.text.strip()

    def uut_status(
        self,
        *,
        token: str,
        serial_number: str,
        start_datetime: Optional[str] = None,
        stop_datetime: Optional[str] = None,
        max_results: Optional[int] = None,
    ) -> Any:
        """Query the `/Uut/Status` endpoint for unit test history."""

        if not serial_number:
            raise ValueError("serial_number must be provided")
        headers = {"Authorization": f"Bearer {token}"}
        payload: Dict[str, Any] = {
            "serialNumber": serial_number,
        }
        if start_datetime:
            payload["startDateTime"] = start_datetime
        if stop_datetime:
            payload["stopDateTime"] = stop_datetime
        if max_results is not None:
            payload["maxNumberOfResults"] = max_results

        try:
            response = self.session.post(
                self._build_url("/Uut/Status"),
                headers=headers,
                json=payload,
                timeout=self.timeout,
            )
            response.raise_for_status()
        except requests.RequestException as exc:  # pragma: no cover - network failure
            raise QrmClientError("Failed to contact QRM UUT status endpoint") from exc

        try:
            return response.json()
        except ValueError:
            return response.text.strip()

    def _build_url(self, path: str) -> str:
        return urljoin(f"{self.base_url}/", path.lstrip("/"))

    @staticmethod
    def _extract_token(response: requests.Response) -> Optional[str]:
        payload: Any
        try:
            payload = response.json()
        except ValueError:
            payload = None

        token = QrmClient._token_from_payload(payload)
        if token:
            return token

        fallback = response.text.strip()
        if fallback:
            token = QrmClient._token_from_payload(fallback)
            if token:
                return token
            return fallback
        return None

    @staticmethod
    def _token_from_payload(payload: Any) -> Optional[str]:
        if isinstance(payload, Dict):
            for key in (
                "token",
                "Token",
                "accessToken",
                "access_token",
                "bearerToken",
                "value",
            ):
                value = payload.get(key)
                if isinstance(value, str) and value.strip():
                    return value.strip()
        if isinstance(payload, str):
            trimmed = payload.strip()
            if not trimmed:
                return None
            if trimmed.startswith("{") and trimmed.endswith("}"):
                try:
                    return QrmClient._token_from_payload(json.loads(trimmed))
                except json.JSONDecodeError:
                    pass
            return trimmed
        return None


__all__ = ["LoginCredentials", "LoginState", "QrmClient", "QrmClientError"]
