"""Ecate LLM application entry point."""

from __future__ import annotations

import argparse
import asyncio
import logging
import signal
from pathlib import Path

import aiohttp
from aiohttp import web

from backend.config import load_config
from backend.dashboard.api import register_dashboard_routes
from backend.db.connection import close_db, init_db
from backend.db.usage import UsageLogger
from backend.proxy.fallback import CircuitBreaker
from backend.proxy.handler import ProxyHandler
from backend.proxy.router import Router

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger("ecate")

# Global reference for signal handlers
_app: web.Application | None = None


async def on_startup(app: web.Application):
    config = app["config"]

    # Database
    await init_db()
    logger.info("Database initialized")

    # HTTP session for upstream requests
    timeout = aiohttp.ClientTimeout(total=300, connect=10)
    app["http_session"] = aiohttp.ClientSession(timeout=timeout)

    # Router (lazy-loads vectors on first use)
    router = Router(config)

    # Circuit breaker
    circuit_breaker = CircuitBreaker(threshold=config.routing.circuit_breaker_threshold)
    app["circuit_breaker"] = circuit_breaker

    # Usage logger
    usage_logger = UsageLogger()
    await usage_logger.start()
    app["usage_logger"] = usage_logger

    # Proxy handler
    handler = ProxyHandler(
        config=config,
        router=router,
        circuit_breaker=circuit_breaker,
        usage_logger=usage_logger,
        http_session=app["http_session"],
    )
    app["handler"] = handler

    logger.info(
        f"Ecate started — {len(config.providers)} providers, "
        f"{len(config.all_models())} models, "
        f"strategy={config.routing.default_strategy}"
    )


async def on_cleanup(app: web.Application):
    if "usage_logger" in app:
        await app["usage_logger"].stop()
    if "http_session" in app:
        await app["http_session"].close()
    await close_db()
    logger.info("Ecate stopped")


async def handle_completions(request: web.Request) -> web.Response:
    handler: ProxyHandler = request.app["handler"]
    return await handler.handle_completions(request)


async def handle_models(request: web.Request) -> web.Response:
    handler: ProxyHandler = request.app["handler"]
    return await handler.handle_models(request)


def create_app(config_path: str = "config.yaml") -> web.Application:
    config = load_config(config_path)

    app = web.Application()
    app["config"] = config

    # OpenAI-compatible API routes
    app.router.add_post("/v1/chat/completions", handle_completions)
    app.router.add_get("/v1/models", handle_models)

    # Dashboard API
    register_dashboard_routes(app)

    # Serve frontend static files (if built)
    static_dir = Path(__file__).parent.parent / "static" / "dashboard"
    if static_dir.exists():
        index_html = static_dir / "index.html"

        async def serve_dashboard_root(request):
            return web.FileResponse(index_html)

        async def serve_dashboard_spa(request):
            """Catch-all for SPA routes — serve index.html for client-side routing."""
            # Check if it's a static asset first
            path = request.match_info.get("path", "")
            if path.startswith("assets/"):
                asset_path = static_dir / path
                if asset_path.exists() and asset_path.is_file():
                    return web.FileResponse(asset_path)
            # Otherwise serve index.html for SPA routing
            return web.FileResponse(index_html)

        # SPA: serve index.html for /dashboard and all sub-paths
        app.router.add_get("/dashboard", serve_dashboard_root)
        app.router.add_get("/dashboard/", serve_dashboard_root)
        app.router.add_get("/dashboard/{path:.*}", serve_dashboard_spa)

        # Static assets (JS, CSS, etc.)
        app.router.add_static("/dashboard/assets", static_dir / "assets")

    app.on_startup.append(on_startup)
    app.on_cleanup.append(on_cleanup)

    return app


def _handle_signal(sig: signal.Signals):
    """Handle shutdown signals gracefully."""
    logger.info(f"Received {sig.name}, initiating graceful shutdown...")
    if _app is not None:
        # Trigger cleanup by raising GracefulExit
        raise web.GracefulExit()


def main():
    global _app

    parser = argparse.ArgumentParser(description="Ecate LLM Router")
    parser.add_argument("--config", default="config.yaml", help="Path to config file")
    parser.add_argument("--host", default=None, help="Override bind host")
    parser.add_argument("--port", type=int, default=None, help="Override bind port")
    args = parser.parse_args()

    app = create_app(args.config)
    _app = app
    config = app["config"]

    host = args.host or config.server.host
    port = args.port or config.server.port

    # Set up signal handlers for graceful shutdown
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    
    for sig in (signal.SIGTERM, signal.SIGINT):
        try:
            loop.add_signal_handler(sig, _handle_signal, sig)
        except NotImplementedError:
            # Windows doesn't support add_signal_handler
            pass

    logger.info(f"Starting Ecate on {host}:{port}")
    web.run_app(app, host=host, port=port, print=None, loop=loop)


if __name__ == "__main__":
    main()
