"""
Thread-safe Setmore API client with token caching.
"""

import threading
from datetime import datetime, timezone, timedelta
from typing import Optional, Dict, Any, List

import requests

from setmore_etl.config import SetmoreConfig


class SetmoreAPIClient:
    """
    Thread-safe Setmore API client optimized for Databricks Serverless.
    
    Handles OAuth token refresh automatically with 5-minute buffer.
    """
    
    def __init__(self, refresh_token: str, config: SetmoreConfig):
        self.refresh_token = refresh_token
        self.config = config
        self._access_token: Optional[str] = None
        self._token_expires_at: Optional[datetime] = None
        self._lock = threading.Lock()
        
    def _get_access_token(self) -> str:
        """Thread-safe token retrieval with caching."""
        if self._access_token and self._token_expires_at:
            if datetime.now(timezone.utc) < self._token_expires_at - timedelta(minutes=5):
                return self._access_token
        
        with self._lock:
            return self._refresh_token_if_needed()
    
    def _refresh_token_if_needed(self) -> str:
        """Refresh token if expired or not present."""
        # Double-check after acquiring lock
        if self._access_token and self._token_expires_at:
            if datetime.now(timezone.utc) < self._token_expires_at - timedelta(minutes=5):
                return self._access_token
        
        url = f"{self.config.base_url}/o/oauth2/token"
        params = {"refreshToken": self.refresh_token}
        
        response = requests.get(url, params=params, timeout=30)
        response.raise_for_status()
        
        data = response.json()
        if not data.get("response"):
            raise SetmoreAPIError(f"Token request failed: {data}")
        
        token_data = data["data"]["token"]
        self._access_token = token_data["access_token"]
        
        expires_in = token_data.get("expires_in", 604799)  # Default 7 days
        self._token_expires_at = datetime.now(timezone.utc) + timedelta(seconds=expires_in)
        
        return self._access_token
    
    def _headers(self) -> Dict[str, str]:
        return {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self._get_access_token()}",
        }
    
    def fetch_appointments_page(
        self,
        start_date: str,
        end_date: str,
        cursor: Optional[str] = None,
        staff_key: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Fetch single page of appointments.
        
        Args:
            start_date: DD-MM-YYYY format
            end_date: DD-MM-YYYY format
            cursor: Pagination cursor from previous response
            staff_key: Filter by specific staff member
        """
        url = f"{self.config.base_url}/bookingapi/appointments"
        params = {
            "startDate": start_date,
            "endDate": end_date,
            "customerDetails": "true",
        }
        
        if staff_key:
            params["staff_key"] = staff_key
        if cursor:
            params["cursor"] = cursor
        
        response = requests.get(url, headers=self._headers(), params=params, timeout=60)
        response.raise_for_status()
        
        return response.json()
    
    def fetch_all_staff(self) -> List[Dict[str, Any]]:
        """Fetch all staff members with pagination."""
        all_staff: List[Dict[str, Any]] = []
        cursor: Optional[str] = None
        
        while True:
            url = f"{self.config.base_url}/bookingapi/staffs"
            params = {"cursor": cursor} if cursor else {}
            
            response = requests.get(url, headers=self._headers(), params=params, timeout=30)
            response.raise_for_status()
            
            data = response.json()
            if data.get("response"):
                all_staff.extend(data["data"].get("staffs", []))
                cursor = data["data"].get("cursor")
                if not cursor:
                    break
            else:
                break
        
        return all_staff
    
    def fetch_all_services(self) -> List[Dict[str, Any]]:
        """Fetch all services."""
        url = f"{self.config.base_url}/bookingapi/services"
        response = requests.get(url, headers=self._headers(), timeout=30)
        response.raise_for_status()
        
        data = response.json()
        if data.get("response"):
            return data["data"].get("services", [])
        return []


class SetmoreAPIError(Exception):
    """Custom exception for Setmore API errors."""
    pass