"""Oura API client with OAuth2 token management."""

import httpx
from typing import Optional, Any
from datetime import datetime, timedelta

from .config import config


class OuraAPIError(Exception):
    """Base exception for Oura API errors."""

    def __init__(self, status_code: int, message: str, detail: Optional[str] = None):
        self.status_code = status_code
        self.message = message
        self.detail = detail
        super().__init__(f"[{status_code}] {message}: {detail}" if detail else f"[{status_code}] {message}")


class OuraClient:
    """Client for interacting with Oura API v2."""

    BASE_URL = "https://api.ouraring.com"

    def __init__(self):
        """Initialize the Oura API client."""
        self.config = config
        self._client = httpx.Client(timeout=30.0)

    def __del__(self):
        """Cleanup HTTP client."""
        if hasattr(self, '_client'):
            self._client.close()

    def _get_headers(self) -> dict[str, str]:
        """Get authorization headers for API requests."""
        if not self.config.access_token:
            raise OuraAPIError(401, "No access token available", "Please configure OURA_ACCESS_TOKEN")
        return {
            "Authorization": f"Bearer {self.config.access_token}",
        }

    def refresh_access_token(self) -> bool:
        """
        Refresh the access token using the refresh token.

        Returns:
            True if refresh was successful, False otherwise
        """
        if not self.config.refresh_token:
            return False

        if not (self.config.client_id and self.config.client_secret):
            return False

        try:
            response = self._client.post(
                f"{self.BASE_URL}/oauth/token",
                data={
                    "grant_type": "refresh_token",
                    "refresh_token": self.config.refresh_token,
                    "client_id": self.config.client_id,
                    "client_secret": self.config.client_secret,
                },
                headers={"Content-Type": "application/x-www-form-urlencoded"},
            )

            if response.status_code == 200:
                data = response.json()
                new_access_token = data.get("access_token")
                new_refresh_token = data.get("refresh_token")

                if new_access_token and new_refresh_token:
                    self.config.save_tokens(new_access_token, new_refresh_token)
                    return True

            return False

        except Exception:
            return False

    def _request(
        self,
        method: str,
        endpoint: str,
        params: Optional[dict[str, Any]] = None,
        retry_on_401: bool = True,
    ) -> dict[str, Any]:
        """
        Make an authenticated request to the Oura API.

        Args:
            method: HTTP method (GET, POST, etc.)
            endpoint: API endpoint path
            params: Query parameters
            retry_on_401: Whether to attempt token refresh on 401 error

        Returns:
            JSON response data

        Raises:
            OuraAPIError: If the API returns an error
        """
        url = f"{self.BASE_URL}{endpoint}"
        headers = self._get_headers()

        try:
            response = self._client.request(
                method=method,
                url=url,
                params=params,
                headers=headers,
            )

            # Handle 401 errors with token refresh
            if response.status_code == 401 and retry_on_401:
                if self.refresh_access_token():
                    # Retry the request once with new token
                    return self._request(method, endpoint, params, retry_on_401=False)

            # Handle error responses
            if response.status_code >= 400:
                try:
                    error_data = response.json()
                    title = error_data.get("title", "API Error")
                    detail = error_data.get("detail", error_data.get("error_description"))
                    raise OuraAPIError(response.status_code, title, detail)
                except (ValueError, KeyError):
                    raise OuraAPIError(
                        response.status_code,
                        f"HTTP {response.status_code}",
                        response.text or None,
                    )

            return response.json()

        except httpx.RequestError as e:
            raise OuraAPIError(0, "Request failed", str(e))

    def _handle_pagination(
        self, endpoint: str, params: Optional[dict[str, Any]] = None
    ) -> list[dict[str, Any]]:
        """
        Handle paginated API responses.

        Args:
            endpoint: API endpoint path
            params: Query parameters

        Returns:
            List of all items from paginated response
        """
        params = params or {}
        all_items = []

        while True:
            data = self._request("GET", endpoint, params)
            items = data.get("data", [])
            all_items.extend(items)

            # Check for next page
            next_token = data.get("next_token")
            if not next_token:
                break

            params["next_token"] = next_token

        return all_items

    # Personal Information
    def get_personal_info(self) -> dict[str, Any]:
        """Get user's personal information."""
        return self._request("GET", "/v2/usercollection/personal_info")

    # Daily Summaries
    def get_daily_sleep(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get daily sleep summaries.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/daily_sleep", params)

    def get_daily_activity(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get daily activity summaries.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/daily_activity", params)

    def get_daily_readiness(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get daily readiness summaries.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/daily_readiness", params)

    def get_daily_stress(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get daily stress summaries.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/daily_stress", params)

    def get_daily_spo2(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get daily SpO2 summaries (Gen 3 ring only).

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/daily_spo2", params)

    def get_daily_resilience(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get daily resilience summaries.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/daily_resilience", params)

    def get_daily_cardiovascular_age(
        self, start_date: str, end_date: Optional[str] = None
    ) -> list[dict[str, Any]]:
        """
        Get daily cardiovascular age predictions.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/daily_cardiovascular_age", params)

    # Detailed Sleep Data
    def get_sleep_periods(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get detailed sleep periods.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/sleep", params)

    def get_sleep_time(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get optimal bedtime recommendations.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/sleep_time", params)

    # Activity & Workouts
    def get_workouts(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get workout summaries.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/workout", params)

    def get_sessions(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get session data (meditation, breathing, etc.).

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/session", params)

    # Time-Series Data
    def get_heartrate(self, start_datetime: str, end_datetime: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get heart rate time-series data (5-minute intervals).

        Args:
            start_datetime: Start datetime in ISO 8601 format
            end_datetime: End datetime in ISO 8601 format (optional)
        """
        params = {"start_datetime": start_datetime}
        if end_datetime:
            params["end_datetime"] = end_datetime
        return self._handle_pagination("/v2/usercollection/heartrate", params)

    # User Annotations
    def get_enhanced_tags(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get user-entered enhanced tags.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/enhanced_tag", params)

    # Device Information
    def get_ring_configuration(self) -> list[dict[str, Any]]:
        """Get ring configuration and device information."""
        return self._handle_pagination("/v2/usercollection/ring_configuration")

    def get_rest_mode_periods(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get rest mode periods.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/rest_mode_period", params)

    # Fitness Metrics
    def get_vo2_max(self, start_date: str, end_date: Optional[str] = None) -> list[dict[str, Any]]:
        """
        Get VO2 max estimates.

        Args:
            start_date: Start date in YYYY-MM-DD format
            end_date: End date in YYYY-MM-DD format (optional)
        """
        params = {"start_date": start_date}
        if end_date:
            params["end_date"] = end_date
        return self._handle_pagination("/v2/usercollection/vO2_max", params)

