"""Local development version of WHOOP connector API with mocked AWS services."""

import os
from typing import Any
from unittest.mock import MagicMock

from fastapi import APIRouter, FastAPI, HTTPException, Query, Request
from fastapi.responses import JSONResponse
from dotenv import load_dotenv

# Load environment - check service directory first, then repo root
# This allows CLI to override via --env flag
if not os.getenv("WHOOP_CLIENT_ID"):  # Only auto-load if not already set
    from pathlib import Path

    repo_root = Path(__file__).parent.parent.parent

    env_files_to_try = [
        Path(".env.local"),  # Service directory
        Path(".env.production"),  # Service directory
        Path(".env"),  # Service directory
        repo_root / ".env.local",  # Repo root (shared)
        repo_root / ".env",  # Repo root (shared)
    ]

    for env_file in env_files_to_try:
        if env_file.exists():
            load_dotenv(env_file)
            break

from synheart_cloud_connector import (
    CloudConnectorError,
    OAuthError,
    RateLimitError,
    WebhookError,
)
from synheart_cloud_connector.vendor_types import RateLimitConfig, VendorConfig, VendorType

# Import but don't initialize real AWS services
# Note: We avoid importing JobQueue here to prevent boto3 import
# We'll use MockJobQueue instead
from synheart_cloud_connector.rate_limit import RateLimiter

# Import SyncCursor type for type hints, but we'll use MockSyncState
try:
    from synheart_cloud_connector.sync_state import SyncCursor
except ImportError:
    # If boto3 not available, create a simple type for local use
    from pydantic import BaseModel
    from typing import Optional

    class SyncCursor(BaseModel):
        vendor: str
        user_id: str
        last_sync_ts: str
        records_synced: int = 0
        last_resource_id: Optional[str] = None
        created_at: str
        updated_at: str


# Mock token store for local testing
class MockTokenStore:
    """Mock token store for local testing without AWS. Persists to disk."""

    def __init__(self, *args, **kwargs):
        import json
        from pathlib import Path

        # Store tokens in __dev__ directory to persist across restarts
        # server/whoop_api.py -> parent is server/ -> parent is CLI_ROOT
        self.tokens_file = Path(__file__).parent.parent / "__dev__" / "tokens.json"
        self.tokens_file.parent.mkdir(parents=True, exist_ok=True)

        # Load existing tokens from disk
        self.tokens = {}
        if self.tokens_file.exists():
            try:
                with open(self.tokens_file, "r") as f:
                    data = json.load(f)
                    # Reconstruct OAuthTokens objects from dict
                    from synheart_cloud_connector.vendor_types import OAuthTokens
                    from datetime import datetime, timezone

                    for key, token_data in data.items():
                        # Parse ISO datetime string back to datetime if present
                        if token_data.get("expires_at"):
                            if isinstance(token_data["expires_at"], str):
                                dt = datetime.fromisoformat(
                                    token_data["expires_at"].replace("Z", "+00:00")
                                )
                                # Ensure timezone-aware
                                if dt.tzinfo is None:
                                    dt = dt.replace(tzinfo=timezone.utc)
                                token_data["expires_at"] = dt
                        self.tokens[key] = OAuthTokens(**token_data)
                print(f"✓ Loaded {len(self.tokens)} token sets from {self.tokens_file}")
            except Exception as e:
                print(f"⚠️  Failed to load tokens from disk: {e}")
                self.tokens = {}

    def _save_to_disk(self):
        """Save tokens to disk."""
        import json
        from datetime import datetime
        from synheart_cloud_connector.vendor_types import OAuthTokens

        # Convert OAuthTokens objects to dict for JSON serialization
        data = {}
        for key, tokens in self.tokens.items():
            # Convert to dict
            token_dict = tokens.model_dump(mode="json")
            # Convert datetime to ISO string if present
            if token_dict.get("expires_at") and isinstance(token_dict["expires_at"], datetime):
                token_dict["expires_at"] = token_dict["expires_at"].isoformat()
            data[key] = token_dict

        try:
            with open(self.tokens_file, "w") as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            print(f"⚠️  Failed to save tokens to disk: {e}")

    def save_tokens(self, vendor, user_id, tokens, vendor_meta=None):
        key = f"{vendor.value}:{user_id}"
        self.tokens[key] = tokens
        print(f"✓ Saved tokens for {key}")
        self._save_to_disk()
        from synheart_cloud_connector.vendor_types import TokenRecord, TokenStatus
        import datetime

        return TokenRecord(
            pk=key,
            sk=datetime.datetime.now().isoformat(),
            access_token="encrypted_token",
            refresh_token="encrypted_refresh",
            expires_at=int(datetime.datetime.now().timestamp()) + 3600,
            scopes=tokens.scopes,
            status=TokenStatus.ACTIVE,
        )

    def get_tokens(self, vendor, user_id):
        key = f"{vendor.value}:{user_id}"
        return self.tokens.get(key)

    def update_last_webhook(self, vendor, user_id):
        print(f"✓ Updated webhook timestamp for {vendor.value}:{user_id}")

    def update_last_pull(self, vendor, user_id):
        print(f"✓ Updated pull timestamp for {vendor.value}:{user_id}")

    def revoke_tokens(self, vendor, user_id):
        key = f"{vendor.value}:{user_id}"
        if key in self.tokens:
            del self.tokens[key]
            self._save_to_disk()
        print(f"✓ Revoked tokens for {key}")


# Mock job queue for local testing
class MockJobQueue:
    """Mock job queue for local testing without AWS SQS."""

    def __init__(self, *args, **kwargs):
        self.messages = []

    def enqueue_event(self, event, delay_seconds=0):
        message_id = f"msg_{len(self.messages)}"
        self.messages.append({"id": message_id, "event": event, "delay": delay_seconds})
        print(f"✓ Enqueued event: {event.event_type} for {event.user_id} (msg_id: {message_id})")
        return message_id


# Mock sync state for local testing
class MockSyncState:
    """Mock sync state for local testing without DynamoDB."""

    def __init__(self, *args, **kwargs):
        self.cursors = {}

    def get_cursor(self, vendor, user_id):
        key = f"{vendor.value}:{user_id}"
        return self.cursors.get(key)

    def update_cursor(self, vendor, user_id, last_sync_ts, records_synced=0, last_resource_id=None):
        from datetime import datetime, timezone

        key = f"{vendor.value}:{user_id}"
        now = datetime.now(timezone.utc).isoformat()

        existing = self.cursors.get(key)
        total_records = (existing.records_synced if existing else 0) + records_synced

        cursor = SyncCursor(
            vendor=vendor.value,
            user_id=user_id,
            last_sync_ts=last_sync_ts,
            records_synced=total_records,
            last_resource_id=last_resource_id,
            created_at=existing.created_at if existing else now,
            updated_at=now,
        )
        self.cursors[key] = cursor
        print(f"✓ Updated sync cursor for {key}: {total_records} records, last_sync={last_sync_ts}")
        return cursor

    def reset_cursor(self, vendor, user_id):
        key = f"{vendor.value}:{user_id}"
        if key in self.cursors:
            del self.cursors[key]
        print(f"✓ Reset sync cursor for {key}")

    def list_cursors(self, vendor=None, limit=100):
        cursors = []
        for key, cursor in self.cursors.items():
            if vendor and cursor.vendor != vendor.value:
                continue
            cursors.append(cursor)
            if len(cursors) >= limit:
                break
        return cursors


from server.whoop_connector import WhoopConnector

# Try to import flux integration (optional)
try:
    from server.flux_integration import (
        is_flux_enabled,
        process_whoop_to_hsi,
        combine_whoop_collections_to_flux_input,
    )

    FLUX_INTEGRATION_AVAILABLE = True
except ImportError:
    FLUX_INTEGRATION_AVAILABLE = False

    def is_flux_enabled() -> bool:
        return False


# Initialize FastAPI app
app = FastAPI(
    title="WHOOP Cloud Connector (Local Dev)",
    description="Local development version with mocked AWS services",
    version="0.1.2-local",
)

# Load configuration from test environment
WHOOP_CLIENT_ID = os.getenv("WHOOP_CLIENT_ID", "")
WHOOP_CLIENT_SECRET = os.getenv("WHOOP_CLIENT_SECRET", "")
WHOOP_WEBHOOK_SECRET = os.getenv("WHOOP_WEBHOOK_SECRET", "")

# Initialize mocked dependencies
token_store = MockTokenStore()
queue = MockJobQueue()
rate_limiter = RateLimiter()
sync_state = MockSyncState()

# Configure WHOOP rate limits
rate_limiter.configure(
    RateLimitConfig(
        vendor=VendorType.WHOOP,
        max_requests=100,
        time_window=60,
        max_burst=120,
    )
)

# Initialize WHOOP connector
whoop_config = VendorConfig(
    vendor=VendorType.WHOOP,
    client_id=WHOOP_CLIENT_ID,
    client_secret=WHOOP_CLIENT_SECRET,
    webhook_secret=WHOOP_WEBHOOK_SECRET,
    base_url="https://api.prod.whoop.com",
    auth_url="https://api.prod.whoop.com/oauth/oauth2/auth",
    token_url="https://api.prod.whoop.com/oauth/oauth2/token",
    scopes=["read:recovery", "read:sleep", "read:workout", "read:cycles", "read:profile", "read:body_measurement"],
)


whoop = WhoopConnector(
    config=whoop_config,
    token_store=token_store,
    queue=queue,
    rate_limiter=rate_limiter,
)

# Create versioned API router
v1_router = APIRouter(prefix="/v1", tags=["v1"])


@app.get("/health")
async def health_check() -> dict[str, Any]:
    """Health check endpoint."""
    health_data = {
        "status": "healthy",
        "version": "0.1.2-local",
        "service": "whoop-cloud-connector",
        "mode": "local_dev",
        "checks": {
            "token_store": "mocked",
            "queue": "mocked",
            "rate_limiter": "ok",
        },
    }

    # Add flux status if available
    if FLUX_INTEGRATION_AVAILABLE:
        health_data["checks"]["flux"] = "available" if is_flux_enabled() else "disabled"
    else:
        health_data["checks"]["flux"] = "not_available"

    return health_data


@v1_router.get("/oauth/authorize")
async def authorize(redirect_uri: str, state: str | None = None) -> dict[str, Any]:
    """
    Get OAuth authorization URL.

    This endpoint generates a URL that points to WHOOP's authorization server.
    The user must visit this URL in their browser to authorize the application.
    """
    try:
        auth_url = whoop.build_authorization_url(
            redirect_uri=redirect_uri,
            state=state,
        )

        return {
            "authorization_url": auth_url,
            "redirect_uri": redirect_uri,
            "state": state or "",
            "note": "Visit this URL in your browser to authorize. After authorization, you'll be redirected back.",
            "instructions": "\n".join(
                [
                    "1. Visit the authorization_url in your browser",
                    "2. Log in to your WHOOP account",
                    "3. Click 'Authorize' to grant access",
                    f"4. You'll be redirected to: {redirect_uri}?code=...&state={state or ''}",
                ]
            ),
        }
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail={
                "error": "Failed to generate authorization URL",
                "message": str(e),
                "vendor": "whoop",
            },
        )


@v1_router.get("/oauth/callback")
async def oauth_callback_get(
    code: str = Query(..., description="Authorization code from vendor"),
    state: str = Query(..., description="State parameter (contains user_id)"),
) -> dict[str, Any]:
    """Handle OAuth callback via GET - uses REAL WHOOP API."""
    try:
        user_id = state
        redirect_uri = os.getenv("WHOOP_REDIRECT_URI", "http://localhost:8000/v1/oauth/callback")

        # Exchange code for REAL tokens from WHOOP
        tokens = await whoop.exchange_code(
            user_id=user_id,
            code=code,
            redirect_uri=redirect_uri,
        )

        return {
            "status": "success",
            "vendor": "whoop",
            "user_id": user_id,
            "expires_in": tokens.expires_in,
            "scopes": tokens.scopes,
            "note": "Real WHOOP tokens obtained and saved",
        }

    except OAuthError as e:
        raise HTTPException(status_code=401, detail=e.to_dict())
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@v1_router.post("/webhooks/whoop")
async def webhook_handler(request: Request) -> JSONResponse:
    """Handle WHOOP webhook events."""
    try:
        headers = dict(request.headers)
        raw_body = await request.body()

        # Process webhook
        message_id = await whoop.process_webhook(headers, raw_body)

        return JSONResponse(
            status_code=200,
            content={
                "status": "success",
                "message_id": message_id,
                "note": "Webhook processed and enqueued (local test mode)",
            },
        )

    except WebhookError as e:
        raise HTTPException(status_code=400, detail=e.to_dict())
    except CloudConnectorError as e:
        raise HTTPException(status_code=500, detail=e.to_dict())


@v1_router.get("/debug/tokens/{user_id}")
async def debug_tokens(user_id: str) -> dict[str, Any]:
    """Debug endpoint to check stored tokens."""
    tokens = token_store.get_tokens(VendorType.WHOOP, user_id)
    if tokens:
        return {
            "user_id": user_id,
            "has_tokens": True,
            "access_token": tokens.access_token[:20] + "...",
            "expires_in": tokens.expires_in,
            "scopes": tokens.scopes,
        }
    else:
        return {
            "user_id": user_id,
            "has_tokens": False,
        }


@v1_router.get("/debug/queue")
async def debug_queue() -> dict[str, Any]:
    """Debug endpoint to check queued messages."""
    return {
        "total_messages": len(queue.messages),
        "messages": queue.messages,
    }


@v1_router.delete("/oauth/disconnect")
async def disconnect_user(
    user_id: str = Query(..., description="User identifier"),
) -> dict[str, Any]:
    """Disconnect user and revoke tokens."""
    try:
        await whoop.revoke_tokens(user_id)
        return {
            "status": "disconnected",
            "vendor": "whoop",
            "user_id": user_id,
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


# ============================================================================
# Data Fetch Endpoints
# ============================================================================


@v1_router.get("/data/{user_id}/recovery")
async def fetch_recovery_collection(
    user_id: str,
    start: str | None = Query(None, description="Start date (ISO8601)"),
    end: str | None = Query(None, description="End date (ISO8601)"),
    limit: int = Query(25, ge=1, le=100, description="Max records (1-100)"),
) -> dict[str, Any]:
    """Fetch collection of recovery records from real WHOOP API."""
    try:
        data = await whoop.fetch_recovery_collection(user_id, start, end, limit)
        return data
    except RateLimitError as e:
        raise HTTPException(status_code=429, detail=e.to_dict())
    except CloudConnectorError as e:
        raise HTTPException(status_code=500, detail=e.to_dict())


@v1_router.get("/data/{user_id}/sleep")
async def fetch_sleep_collection(
    user_id: str,
    start: str | None = Query(None, description="Start date (ISO8601)"),
    end: str | None = Query(None, description="End date (ISO8601)"),
    limit: int = Query(25, ge=1, le=100, description="Max records (1-100)"),
) -> dict[str, Any]:
    """Fetch collection of sleep records from real WHOOP API."""
    try:
        data = await whoop.fetch_sleep_collection(user_id, start, end, limit)
        return data
    except RateLimitError as e:
        raise HTTPException(status_code=429, detail=e.to_dict())
    except CloudConnectorError as e:
        raise HTTPException(status_code=500, detail=e.to_dict())


@v1_router.get("/data/{user_id}/workouts")
async def fetch_workout_collection(
    user_id: str,
    start: str | None = Query(None, description="Start date (ISO8601)"),
    end: str | None = Query(None, description="End date (ISO8601)"),
    limit: int = Query(25, ge=1, le=100, description="Max records (1-100)"),
) -> dict[str, Any]:
    """Fetch collection of workout records from real WHOOP API."""
    try:
        data = await whoop.fetch_workout_collection(user_id, start, end, limit)
        return data
    except RateLimitError as e:
        raise HTTPException(status_code=429, detail=e.to_dict())
    except CloudConnectorError as e:
        raise HTTPException(status_code=500, detail=e.to_dict())


@v1_router.get("/data/{user_id}/cycles")
async def fetch_cycle_collection(
    user_id: str,
    start: str | None = Query(None, description="Start date (ISO8601)"),
    end: str | None = Query(None, description="End date (ISO8601)"),
    limit: int = Query(25, ge=1, le=100, description="Max records (1-100)"),
) -> dict[str, Any]:
    """Fetch collection of cycle records from real WHOOP API."""
    try:
        data = await whoop.fetch_cycle_collection(user_id, start, end, limit)
        return data
    except RateLimitError as e:
        raise HTTPException(status_code=429, detail=e.to_dict())
    except CloudConnectorError as e:
        raise HTTPException(status_code=500, detail=e.to_dict())


@v1_router.get("/data/{user_id}/profile")
async def fetch_user_profile(user_id: str) -> dict[str, Any]:
    """Fetch user profile from real WHOOP API."""
    try:
        data = await whoop.fetch_user_profile(user_id)
        return data
    except RateLimitError as e:
        raise HTTPException(status_code=429, detail=e.to_dict())
    except CloudConnectorError as e:
        raise HTTPException(status_code=500, detail=e.to_dict())


# ============================================================================
# Puller Endpoint (Backfill with Cursor Tracking)
# ============================================================================


@v1_router.post("/pull/{user_id}")
async def pull_data(
    user_id: str,
    resource_types: list[str] | None = Query(
        None,
        description="Resource types to pull (recovery, sleep, workout, cycle). Defaults to all.",
    ),
    since: str | None = Query(
        None, description="Start date (ISO8601 format). If not provided, uses last sync cursor."
    ),
    limit: int = Query(25, ge=1, le=100, description="Maximum records per resource type"),
) -> dict[str, Any]:
    """
    Pull (backfill) user data from WHOOP with cursor tracking.

    This endpoint performs incremental data pulls using sync_state cursors
    to track the last sync timestamp. It fetches data from WHOOP and updates
    the cursor after successful pulls.

    Query parameters:
    - resource_types: Optional list of resource types (recovery, sleep, workout, cycle)
      If not provided, pulls all types.
    - since: Optional start date (ISO8601 format, e.g., "2024-01-01T00:00:00Z")
      If not provided, uses the last sync cursor timestamp.
    - limit: Maximum number of records per resource type (default 25, max 100)

    Returns:
        Summary of pulled data with record counts per resource type
    """
    try:
        from datetime import datetime, timezone, timedelta

        # Default to all resource types
        if not resource_types:
            resource_types = ["recovery", "sleep", "workout", "cycle"]

        # Get last sync cursor if since not provided
        if not since:
            cursor = sync_state.get_cursor(VendorType.WHOOP, user_id)
            if cursor:
                since = cursor.last_sync_ts
                pull_type = "incremental"
            else:
                # First sync - default to last 7 days
                since_dt = datetime.now(timezone.utc) - timedelta(days=7)
                since = since_dt.isoformat()
                pull_type = "initial"
        else:
            pull_type = "manual"

        # Pull data from each resource type
        results = {}
        total_records = 0
        now = datetime.now(timezone.utc).isoformat()

        for resource_type in resource_types:
            try:
                if resource_type == "recovery":
                    data = await whoop.fetch_recovery_collection(user_id, start=since, limit=limit)
                elif resource_type == "sleep":
                    data = await whoop.fetch_sleep_collection(user_id, start=since, limit=limit)
                elif resource_type == "workout":
                    data = await whoop.fetch_workout_collection(user_id, start=since, limit=limit)
                elif resource_type == "cycle":
                    data = await whoop.fetch_cycle_collection(user_id, start=since, limit=limit)
                else:
                    results[resource_type] = {"error": "Unknown resource type"}
                    continue

                # Count records
                records = data.get("records", [])
                record_count = len(records)
                total_records += record_count

                results[resource_type] = {
                    "records": record_count,
                    "data": records,  # Include actual data for now (will be stored in S3 later)
                }

            except (RateLimitError, CloudConnectorError) as e:
                results[resource_type] = {"error": str(e), "error_type": type(e).__name__}
            except Exception as e:
                # Catch any other exceptions (e.g., OAuthError, network errors)
                import traceback

                results[resource_type] = {
                    "error": str(e),
                    "error_type": type(e).__name__,
                    "traceback": traceback.format_exc(),  # Always include for debugging
                }

        # Update sync cursor with new timestamp
        sync_state.update_cursor(
            VendorType.WHOOP,
            user_id,
            last_sync_ts=now,
            records_synced=total_records,
        )

        response_data = {
            "status": "success",
            "vendor": "whoop",
            "user_id": user_id,
            "pull_type": pull_type,
            "since": since,
            "pulled_at": now,
            "total_records": total_records,
            "results": results,
            "note": "Data pulled from real WHOOP API (local dev mode with mocked cursor tracking)",
        }

        # Optionally convert to HSI format if flux is enabled
        if FLUX_INTEGRATION_AVAILABLE and is_flux_enabled():
            try:
                # Combine all collections for flux processing
                flux_input = combine_whoop_collections_to_flux_input(
                    recovery=results.get("recovery", {}).get("data", []),
                    sleep=results.get("sleep", {}).get("data", []),
                    workout=results.get("workout", {}).get("data", []),
                    cycle=results.get("cycle", {}).get("data", []),
                )

                # Get timezone from user profile or default to UTC
                user_tz = "UTC"  # Could fetch from user profile
                device_id = f"whoop-{user_id}"

                hsi_payloads = process_whoop_to_hsi(flux_input, user_tz, device_id)
                response_data["hsi_payloads"] = hsi_payloads
                response_data["hsi_count"] = len(hsi_payloads)
            except Exception as e:
                # Don't fail the pull if HSI conversion fails, just log it
                response_data["hsi_error"] = str(e)

        return response_data

    except RateLimitError as e:
        raise HTTPException(status_code=429, detail=e.to_dict())
    except CloudConnectorError as e:
        raise HTTPException(status_code=500, detail=e.to_dict())
    except Exception as e:
        # Catch any unexpected errors and return them
        import traceback

        raise HTTPException(
            status_code=500,
            detail={
                "error": str(e),
                "error_type": type(e).__name__,
                "traceback": (
                    traceback.format_exc()
                    if os.getenv("DEBUG", "false").lower() == "true"
                    else None
                ),
            },
        )


# ============================================================================
# HSI Conversion Endpoints (Flux)
# ============================================================================


@v1_router.post("/hsi/{user_id}/convert")
async def convert_to_hsi(
    user_id: str,
    request: Request,
    timezone: str = Query("UTC", description="User's timezone (e.g., America/New_York)"),
    device_id: str | None = Query(
        None, description="Device identifier (defaults to whoop-{user_id})"
    ),
) -> dict[str, Any]:
    """
    Convert WHOOP data to HSI format using Flux.

    This endpoint accepts WHOOP data (either via request body or by fetching from API)
    and converts it to HSI-compliant JSON using the Flux processing engine.

    Requires: USE_FLUX=true environment variable
    """
    if not FLUX_INTEGRATION_AVAILABLE:
        raise HTTPException(
            status_code=503,
            detail={
                "error": "Flux integration not available",
                "message": "Flux binary integration not available",
            },
        )

    if not is_flux_enabled():
        raise HTTPException(
            status_code=400,
            detail={
                "error": "Flux processing is disabled",
                "message": "Set USE_FLUX=true environment variable to enable",
            },
        )

    try:
        # Try to get data from request body
        body_data = {}
        try:
            body_data = await request.json()
        except Exception:
            pass  # No body data, will fetch from API

        recovery = body_data.get("recovery")
        sleep = body_data.get("sleep")
        workout = body_data.get("workout")
        cycle = body_data.get("cycle")

        # If no data provided in body, fetch from API
        if not any([recovery, sleep, workout, cycle]):
            # Fetch all collections
            recovery_data = await whoop.fetch_recovery_collection(user_id, limit=25)
            sleep_data = await whoop.fetch_sleep_collection(user_id, limit=25)
            workout_data = await whoop.fetch_workout_collection(user_id, limit=25)
            cycle_data = await whoop.fetch_cycle_collection(user_id, limit=25)

            recovery = recovery_data.get("records", [])
            sleep = sleep_data.get("records", [])
            workout = workout_data.get("records", [])
            cycle = cycle_data.get("records", [])

        # Combine collections for flux
        flux_input = combine_whoop_collections_to_flux_input(
            recovery=recovery or [],
            sleep=sleep or [],
            workout=workout or [],
            cycle=cycle or [],
        )

        if device_id is None:
            device_id = f"whoop-{user_id}"

        # Convert to HSI
        hsi_payloads = process_whoop_to_hsi(flux_input, timezone, device_id)

        return {
            "status": "success",
            "vendor": "whoop",
            "user_id": user_id,
            "timezone": timezone,
            "device_id": device_id,
            "hsi_payloads": hsi_payloads,
            "hsi_count": len(hsi_payloads),
            "input_counts": {
                "recovery": len(flux_input.get("recovery", [])),
                "sleep": len(flux_input.get("sleep", [])),
                "workout": len(flux_input.get("workout", [])),
                "cycle": len(flux_input.get("cycle", [])),
            },
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail={"error": str(e)})
    except Exception as e:
        import traceback

        raise HTTPException(
            status_code=500,
            detail={
                "error": str(e),
                "error_type": type(e).__name__,
                "traceback": (
                    traceback.format_exc()
                    if os.getenv("DEBUG", "false").lower() == "true"
                    else None
                ),
            },
        )


# Include versioned router
app.include_router(v1_router)


@app.exception_handler(CloudConnectorError)
async def cloud_connector_error_handler(request: Request, exc: CloudConnectorError) -> JSONResponse:
    """Handle CloudConnectorError exceptions."""
    return JSONResponse(
        status_code=500,
        content=exc.to_dict(),
    )


if __name__ == "__main__":
    import uvicorn

    print("🚀 Starting WHOOP connector in LOCAL DEV mode")
    print("📝 Using mocked AWS services (DynamoDB, SQS, KMS)")
    print("🔗 Server running at: http://localhost:8000")
    print("📖 API docs at: http://localhost:8000/docs")
    uvicorn.run(app, host="0.0.0.0", port=8000)
