"""
Shangrilux SDK per Python
Versione: 1.0.0
Documentazione: https://shangrilux.com/developers

Requisiti: pip install requests

Utilizzo:
    from shangrilux_sdk import ShangriluxClient

    client = ShangriluxClient(api_key='ostx_...')
    bookings = client.bookings.list(status='confirmed')
    print(bookings['data'])
"""
import json
from typing import Optional
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

BASE_URL = "https://guest-portal-pro.preview.emergentagent.com"


class ShangriluxError(Exception):
    """Errore restituito dall'API Shangrilux."""
    def __init__(self, message: str, status_code: int = None, body: dict = None):
        super().__init__(message)
        self.status_code = status_code
        self.body = body or {}


class ShangriluxClient:
    """
    Client HTTP per l'API pubblica Shangrilux v1.

    Args:
        api_key: La tua API Key (formato: ostx_...)
        base_url: URL base della tua istanza Shangrilux (default: https://shangrilux.com)
        timeout: Timeout per le richieste in secondi (default: 15)
        max_retries: Numero di retry automatici su errori 5xx/timeout (default: 2)
    """

    def __init__(
        self,
        api_key: str,
        base_url: str = BASE_URL,
        timeout: int = 15,
        max_retries: int = 2,
    ):
        if not api_key or not api_key.startswith("ostx_"):
            raise ShangriluxError("API Key non valida. Deve iniziare con 'ostx_'")

        self._api_key = api_key
        self._base_url = base_url.rstrip("/")
        self._timeout = timeout

        # Session con retry automatico
        self._session = requests.Session()
        retry = Retry(
            total=max_retries,
            backoff_factor=0.5,
            status_forcelist=[500, 502, 503, 504],
            allowed_methods=["GET"],
        )
        adapter = HTTPAdapter(max_retries=retry)
        self._session.mount("https://", adapter)
        self._session.mount("http://", adapter)
        self._session.headers.update({
            "X-API-Key": self._api_key,
            "Content-Type": "application/json",
            "User-Agent": "Shangrilux-Python-SDK/1.0",
        })

        # Sub-client
        self.properties = _PropertiesAPI(self)
        self.bookings = _BookingsAPI(self)
        self.access = _AccessAPI(self)

    def _request(self, method: str, path: str, params: dict = None, body: dict = None) -> dict:
        url = f"{self._base_url}/api/v1{path}"
        try:
            resp = self._session.request(
                method,
                url,
                params={k: v for k, v in (params or {}).items() if v is not None},
                json=body,
                timeout=self._timeout,
            )
        except requests.Timeout:
            raise ShangriluxError(f"Timeout ({self._timeout}s) sulla richiesta {method} {path}", 408)
        except requests.ConnectionError as e:
            raise ShangriluxError(f"Errore di connessione: {e}")

        try:
            data = resp.json()
        except ValueError:
            data = {"raw": resp.text}

        if not resp.ok:
            raise ShangriluxError(
                data.get("detail", f"HTTP {resp.status_code}"),
                resp.status_code,
                data,
            )

        # Aggiunge info rate limit come attributo del dict ritornato
        result = dict(data)
        result["_rate_limit"] = {
            "limit": resp.headers.get("X-RateLimit-Limit"),
            "remaining": resp.headers.get("X-RateLimit-Remaining"),
            "reset": resp.headers.get("X-RateLimit-Reset"),
        }
        return result

    def info(self) -> dict:
        """Informazioni sull'API e sul tenant autenticato."""
        return self._request("GET", "/")


class _PropertiesAPI:
    def __init__(self, client: ShangriluxClient):
        self._c = client

    def list(self) -> dict:
        """Lista tutte le proprietà del tenant."""
        return self._c._request("GET", "/properties")

    def get(self, property_id: str) -> dict:
        """Dettaglio singola proprietà per ID MongoDB o slug."""
        return self._c._request("GET", f"/properties/{property_id}")


class _BookingsAPI:
    def __init__(self, client: ShangriluxClient):
        self._c = client

    def list(
        self,
        status: Optional[str] = None,
        limit: int = 50,
        offset: int = 0,
    ) -> dict:
        """
        Lista prenotazioni con filtri.

        Args:
            status: Filtra per stato ('pending', 'confirmed', 'cancelled', 'revoked')
            limit: Max 200 risultati per pagina
            offset: Offset per paginazione
        """
        return self._c._request("GET", "/bookings", params={
            "status": status, "limit": limit, "offset": offset
        })

    def get(self, booking_id: str) -> dict:
        """Dettaglio prenotazione per ID MongoDB o external_id."""
        return self._c._request("GET", f"/bookings/{booking_id}")

    def create(
        self,
        property_id: str,
        guest_name: str,
        guest_email: str,
        checkin_at: str,
        checkout_at: str,
        guest_phone: str = "",
        num_guests: int = 1,
        total_price: float = 0.0,
        notes: str = "",
        source: str = "pms",
        external_id: str = "",
    ) -> dict:
        """
        Crea una nuova prenotazione.
        Se external_id è già presente, viene restituita quella esistente (deduplicazione).

        Args:
            property_id: ID MongoDB o slug della proprietà
            guest_name: Nome completo dell'ospite
            guest_email: Email dell'ospite
            checkin_at: Data check-in (ISO 8601, es. '2026-06-01')
            checkout_at: Data check-out (ISO 8601, es. '2026-06-05')
            external_id: ID univoco nel tuo PMS per deduplicazione
        """
        return self._c._request("POST", "/bookings", body={
            "property_id": property_id,
            "guest_name": guest_name,
            "guest_email": guest_email,
            "guest_phone": guest_phone,
            "num_guests": num_guests,
            "checkin_at": checkin_at,
            "checkout_at": checkout_at,
            "total_price": total_price,
            "notes": notes,
            "source": source,
            "external_id": external_id,
        })

    def cancel(self, booking_id: str) -> dict:
        """Cancella una prenotazione attiva."""
        return self._c._request("PATCH", f"/bookings/{booking_id}/cancel")


class _AccessAPI:
    def __init__(self, client: ShangriluxClient):
        self._c = client

    def list(self) -> dict:
        """Lista tutti i dispositivi di accesso (serrature, lettori, ecc.)."""
        return self._c._request("GET", "/access/devices")

    def get(self, device_id: str) -> dict:
        """Stato e dettaglio di un dispositivo per ID o slug."""
        return self._c._request("GET", f"/access/devices/{device_id}")

    def command(
        self,
        device_id: str,
        command: str,
        pin: Optional[str] = None,
        duration_seconds: Optional[int] = None,
    ) -> dict:
        """
        Invia un comando al dispositivo.

        Args:
            device_id: ID MongoDB o slug del dispositivo
            command: 'lock', 'unlock', o 'status'
            pin: PIN di sicurezza (opzionale)
            duration_seconds: Durata unlock temporaneo in secondi (opzionale)
        """
        body = {"command": command}
        if pin:
            body["pin"] = pin
        if duration_seconds:
            body["duration_seconds"] = duration_seconds
        return self._c._request("POST", f"/access/devices/{device_id}/command", body=body)

    def unlock(self, device_id: str, **kwargs) -> dict:
        """Apri serratura."""
        return self.command(device_id, "unlock", **kwargs)

    def lock(self, device_id: str, **kwargs) -> dict:
        """Chiudi serratura."""
        return self.command(device_id, "lock", **kwargs)

    def status(self, device_id: str) -> dict:
        """Leggi stato del dispositivo."""
        return self.command(device_id, "status")


# ══════════════════════════════════════════════════════════════════════════
# ESEMPIO DI UTILIZZO COMPLETO
# ══════════════════════════════════════════════════════════════════════════
if __name__ == "__main__":
    client = ShangriluxClient(api_key="ostx_YOUR_API_KEY")

    # 1. Info API
    info = client.info()
    print(f"Connesso come: {info['tenant']} ({info['plan']})")
    print(f"Rate limit: {info['rate_limit_per_hour']} req/h")

    # 2. Lista proprietà
    props = client.properties.list()
    print(f"\n{props['total']} proprietà:")
    for p in props["data"]:
        print(f"  - {p['name']} ({p['city']}, {p['max_guests']} ospiti max)")

    # 3. Crea prenotazione da PMS
    booking = client.bookings.create(
        property_id="villa-belvedere",
        guest_name="Mario Rossi",
        guest_email="mario@hotel.it",
        guest_phone="+39 333 1234567",
        num_guests=2,
        checkin_at="2026-06-15",
        checkout_at="2026-06-20",
        total_price=1500.00,
        source="pms",
        external_id="RES-12345",
    )
    print(f"\nPrenotazione: {booking['data']['id']} — {'NUOVA' if booking['created'] else 'ESISTENTE'}")

    # 4. Apri serratura al check-in
    result = client.access.unlock("porta-principale")
    print(f"\nSerratura: {result['data']['status']}")

    # 5. Lista prenotazioni confermate
    confirmed = client.bookings.list(status="confirmed", limit=5)
    print(f"\nPrenotazioni confermate: {confirmed['total']}")
