from __future__ import annotations

from typing import Any, Dict, Optional

import requests

API_BASE_URL = "https://api.veriflo.app/v1"
DEFAULT_TIMEOUT_SECONDS = 10


class VerifloError(Exception):
    def __init__(
        self,
        message: str,
        status_code: Optional[int] = None,
        code: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ) -> None:
        super().__init__(message)
        self.status_code = status_code
        self.code = code
        self.data = data or {}


class VerifloClient:
    def __init__(
        self,
        api_key: str,
        timeout: float = DEFAULT_TIMEOUT_SECONDS,
        base_url: str = API_BASE_URL,
    ) -> None:
        if not api_key or not isinstance(api_key, str):
            raise ValueError("Veriflo: api_key is required")
        self.api_key = api_key.strip()
        self.timeout = float(timeout)
        self.base_url = str(base_url).rstrip("/")

    def send_otp(
        self,
        phone: str,
        otp: Optional[str] = None,
        integration_test: bool = False,
        test_mode: bool = False,
    ) -> Dict[str, Any]:
        payload: Dict[str, Any] = {"phone": str(phone or "")}
        if not payload["phone"]:
            raise ValueError("Veriflo: phone is required for send_otp")
        if otp is not None:
            payload["otp"] = str(otp)
        if integration_test:
            payload["integration_test"] = True
        if test_mode:
            payload["test_mode"] = True
        return self._request("POST", "/otp/send", payload)

    def resend_otp(
        self,
        request_id: str,
        otp: Optional[str] = None,
        integration_test: bool = False,
        test_mode: bool = False,
    ) -> Dict[str, Any]:
        if not request_id:
            raise ValueError("Veriflo: request_id is required for resend_otp")
        payload: Dict[str, Any] = {"request_id": str(request_id)}
        if otp is not None:
            payload["otp"] = str(otp)
        if integration_test:
            payload["integration_test"] = True
        if test_mode:
            payload["test_mode"] = True
        return self._request("POST", "/otp/resend", payload)

    def verify_otp(self, request_id: str, otp: str) -> Dict[str, Any]:
        if not request_id:
            raise ValueError("Veriflo: request_id is required for verify_otp")
        if otp is None or str(otp) == "":
            raise ValueError("Veriflo: otp is required for verify_otp")
        return self._request(
            "POST",
            "/otp/verify",
            {"request_id": str(request_id), "otp": str(otp)},
        )

    @staticmethod
    def verify_otp_locally(expected_otp: str, received_otp: str) -> bool:
        expected = str(expected_otp or "")
        received = str(received_otp or "")
        return expected.strip() != "" and expected == received

    def _request(self, method: str, path: str, payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        url = f"{self.base_url}{path}"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }
        try:
            response = requests.request(
                method=method,
                url=url,
                json=payload,
                headers=headers,
                timeout=self.timeout,
            )
        except requests.RequestException as exc:
            raise VerifloError(f"Veriflo: network error: {exc}") from exc

        try:
            data = response.json()
        except ValueError:
            data = {}

        if not response.ok:
            raise VerifloError(
                data.get("error") or f"HTTP {response.status_code}",
                status_code=response.status_code,
                code=data.get("code"),
                data=data,
            )

        return data
