"""Synchronous Telegram Gateway API client."""

from __future__ import annotations

import re
from typing import Any

import requests

from .exceptions import (
    TelegramGatewayAPIError,
    TelegramGatewayHTTPError,
    TelegramGatewayValidationError,
)
from .models import RequestStatus

BASE_URL = "https://gatewayapi.telegram.org"
E164_RE = re.compile(r"^\+[1-9]\d{1,14}$")


class TelegramGatewayClient:
    """Client for Telegram Gateway API.

    Docs: https://core.telegram.org/gateway/api
    """

    def __init__(
        self,
        api_token: str,
        base_url: str = BASE_URL,
        timeout: float = 10.0,
        session: requests.Session | None = None,
    ) -> None:
        if not api_token:
            raise TelegramGatewayValidationError("api_token must not be empty")

        self.api_token = api_token
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout
        self.session = session or requests.Session()
        self.session.headers.update(
            {
                "Authorization": f"Bearer {self.api_token}",
                "Content-Type": "application/json",
            }
        )

    def send_verification_message(
        self,
        *,
        phone_number: str,
        request_id: str | None = None,
        sender_username: str | None = None,
        code: str | None = None,
        code_length: int | None = None,
        callback_url: str | None = None,
        payload: str | None = None,
        ttl: int | None = None,
    ) -> RequestStatus:
        """Send verification message to a Telegram user."""
        self._validate_phone_number(phone_number)

        if code is not None:
            if not code.isdigit() or not 4 <= len(code) <= 8:
                raise TelegramGatewayValidationError(
                    "code must be a numeric string with length from 4 to 8"
                )
        elif code_length is not None and not 4 <= code_length <= 8:
            raise TelegramGatewayValidationError("code_length must be in range [4, 8]")

        if ttl is not None and not 30 <= ttl <= 3600:
            raise TelegramGatewayValidationError("ttl must be in range [30, 3600]")

        if callback_url is not None and not callback_url.startswith("https://"):
            raise TelegramGatewayValidationError("callback_url must start with https://")

        body = {
            "phone_number": phone_number,
            "request_id": request_id,
            "sender_username": sender_username,
            "code": code,
            "code_length": code_length,
            "callback_url": callback_url,
            "payload": payload,
            "ttl": ttl,
        }
        result = self._call("sendVerificationMessage", body)
        return RequestStatus.from_dict(result)

    def check_send_ability(self, *, phone_number: str) -> RequestStatus:
        """Check if Telegram can deliver a verification code to phone_number."""
        self._validate_phone_number(phone_number)
        result = self._call("checkSendAbility", {"phone_number": phone_number})
        return RequestStatus.from_dict(result)

    def check_verification_status(
        self, *, request_id: str, code: str | None = None
    ) -> RequestStatus:
        """Check status of sent verification or validate user-entered code."""
        if not request_id:
            raise TelegramGatewayValidationError("request_id must not be empty")

        if code is not None and (not code.isdigit() or not 4 <= len(code) <= 8):
            raise TelegramGatewayValidationError(
                "code must be a numeric string with length from 4 to 8"
            )

        result = self._call(
            "checkVerificationStatus",
            {
                "request_id": request_id,
                "code": code,
            },
        )
        return RequestStatus.from_dict(result)

    def revoke_verification_message(self, *, request_id: str) -> bool:
        """Request revocation for a verification message by request_id."""
        if not request_id:
            raise TelegramGatewayValidationError("request_id must not be empty")

        result = self._call("revokeVerificationMessage", {"request_id": request_id})
        return bool(result)

    def _call(self, method: str, body: dict[str, Any]) -> Any:
        url = f"{self.base_url}/{method}"
        payload = {k: v for k, v in body.items() if v is not None}

        try:
            response = self.session.post(url, json=payload, timeout=self.timeout)
        except requests.RequestException as exc:
            raise TelegramGatewayHTTPError(str(exc)) from exc

        try:
            data = response.json()
        except ValueError as exc:
            raise TelegramGatewayHTTPError(
                f"Invalid JSON response (HTTP {response.status_code})"
            ) from exc

        if not isinstance(data, dict):
            raise TelegramGatewayHTTPError("Unexpected response format")

        if data.get("ok") is True:
            return data.get("result")

        raise TelegramGatewayAPIError(
            error=str(data.get("error", "Unknown API error")),
            status_code=response.status_code,
        )

    @staticmethod
    def _validate_phone_number(phone_number: str) -> None:
        if not E164_RE.fullmatch(phone_number):
            raise TelegramGatewayValidationError(
                "phone_number must be in E.164 format (e.g. +15551234567)"
            )
