"""FastAPI application entry point. Data from live Xero API."""
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from contextlib import asynccontextmanager
import logging
import asyncio
import requests
from pathlib import Path
from dotenv import load_dotenv
from starlette.middleware.base import BaseHTTPMiddleware

_ROOT_DIR = Path(__file__).resolve().parent.parent.parent
_env_path = _ROOT_DIR / ".env"
if _env_path.exists():
    load_dotenv(_env_path)

from app.database import init_db
from app.routers import invoices, bi, admin, auth, directors, performance, custom_pages, custom_page_public
from app.services.sync_service import sync_service
from app.services.xero_service import xero_service
from app.services.xero_cache import xero_cache
import os

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

_SITE_UPGRADE_EXEMPT = frozenset({"/health", "/api/site-status"})


def site_upgrade_enabled() -> bool:
    """When true, API returns 503 except health + public site-status (see .env SITE_UPGRADE_MODE)."""
    v = os.getenv("SITE_UPGRADE_MODE", "").strip().lower()
    return v in ("1", "true", "yes", "on")


class SiteUpgradeMiddleware(BaseHTTPMiddleware):
    """Lock down API during maintenance; SPA reads /api/site-status first."""

    async def dispatch(self, request: Request, call_next):
        if request.method == "OPTIONS":
            return await call_next(request)
        if not site_upgrade_enabled():
            return await call_next(request)
        path = request.url.path
        if path in _SITE_UPGRADE_EXEMPT:
            return await call_next(request)
        return JSONResponse(
            status_code=503,
            content={
                "detail": "Site upgrade in progress — please try again later.",
                "upgrade_mode": True,
            },
        )


async def refresh_xero_token_periodically():
    while True:
        try:
            await asyncio.sleep(25 * 60)
            logger.info("🔄 Proactively refreshing Xero access token...")
            xero_service._clear_access_token()
            token = xero_service._get_access_token(force_refresh=True)
            logger.info(f"✅ Xero token refreshed successfully (length: {len(token)})")
            xero_cache.log_stats()
        except Exception as e:
            logger.error(f"❌ Error refreshing Xero token: {e}", exc_info=True)
            await asyncio.sleep(5 * 60)

@asynccontextmanager
async def lifespan(app: FastAPI):
    logger.info("=" * 60)
    logger.info("🚀 Starting Nixon KPI Stats API")
    logger.info("=" * 60)
    
    init_db()
    if site_upgrade_enabled():
        logger.warning("🚧 SITE_UPGRADE_MODE is ON — API locked to health + /api/site-status until disabled")
    logger.info("📊 Data Source: Live Xero API")
    logger.info("💡 CSV imports retired - all data comes from Xero")
    _client_id = os.getenv("CLIENT_ID", "")
    logger.info(f"🔑 Xero App Client ID: {_client_id or 'not set'}")
    token_refresh_task = asyncio.create_task(refresh_xero_token_periodically())
    logger.info("🔄 Xero token auto-refresh enabled (every 25 minutes)")
    logger.info("=" * 60)
    
    yield
    logger.info("=" * 60)
    logger.info("⏹️ Shutting down Nixon KPI Stats API")
    token_refresh_task.cancel()
    try:
        await token_refresh_task
    except asyncio.CancelledError:
        pass
    logger.info("=" * 60)

app = FastAPI(
    title="Nixon KPI Stats API",
    description="Backend API for Nixon KPI dashboard with simPRO invoice data",
    version="1.0.0",
    lifespan=lifespan
)
_cors_origins = ["http://localhost:3000", "http://127.0.0.1:3000"]
_extra = os.getenv("CORS_ORIGINS", "").strip()
if _extra:
    _cors_origins.extend(o.strip() for o in _extra.split(",") if o.strip())
app.add_middleware(
    CORSMiddleware,
    allow_origins=_cors_origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
app.add_middleware(SiteUpgradeMiddleware)
@app.exception_handler(requests.exceptions.HTTPError)
async def http_error_handler(request: Request, exc: requests.exceptions.HTTPError):
    retry_after = getattr(exc, 'retry_after', None)
    status_code = exc.response.status_code if exc.response else 500
    
    headers = {}
    if retry_after and status_code == 429:
        headers["Retry-After"] = str(retry_after)
        logger.warning(f"⏱️ Rate limit exceeded. Client should retry after {retry_after}s")
    
    return JSONResponse(
        status_code=status_code,
        content={
            "detail": str(exc),
            "retry_after": retry_after if retry_after else None
        },
        headers=headers
    )
app.include_router(auth.router)
app.include_router(invoices.router)
app.include_router(bi.router)
app.include_router(admin.router)
app.include_router(custom_pages.router)
app.include_router(custom_page_public.router)
app.include_router(directors.router)
app.include_router(performance.router)

@app.get("/health")
async def health_check():
    return {
        "status": "healthy",
        "service": "Nixon KPI Stats API",
        "version": "1.0.0",
        "upgrade_mode": site_upgrade_enabled(),
    }


@app.get("/api/site-status")
async def site_status():
    """Public: lets the SPA decide whether to show maintenance before calling authenticated APIs."""
    return {"upgrade_mode": site_upgrade_enabled()}


@app.get("/")
async def root():
    return {
        "message": "Nixon KPI Stats API",
        "version": "1.0.0",
        "docs": "/docs"
    }

