"""FastAPI web application for fithia2 GUI."""
from __future__ import annotations

import os
import signal
import sys
from contextlib import asynccontextmanager
from pathlib import Path

# ── Python 3.13 + uvicorn SIGINT workaround ───────────────────────────────────
# In Python 3.13 asyncio's Runner._on_sigint cancels the running task when it
# sees a re-raised SIGINT from uvicorn's capture_signals(), causing a spurious
# CancelledError traceback during lifespan shutdown.  Suppressing the re-raise
# is safe because uvicorn has already set should_exit=True before calling it.
if sys.version_info >= (3, 13):
    _orig_raise = signal.raise_signal

    def _no_sigint_reraise(sig: int) -> None:
        if sig != signal.SIGINT:
            _orig_raise(sig)

    signal.raise_signal = _no_sigint_reraise  # type: ignore[assignment]

# ── Suppress uvicorn's "Interrupted by SIGINT" error log on clean shutdown ────
import logging as _logging


class _SuppressSigintLog(_logging.Filter):
    def filter(self, record: _logging.LogRecord) -> bool:
        return "Interrupted by SIGINT" not in record.getMessage()


_logging.getLogger("uvicorn.error").addFilter(_SuppressSigintLog())

try:
    from dotenv import load_dotenv
    load_dotenv()
except ImportError:
    pass

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

from apps.web.routers import backtest, docs, experiments, leaderboard, paper_trading, runs, sqs

_STATIC_DIR = Path(__file__).parent / "static"


@asynccontextmanager
async def lifespan(app: FastAPI):  # type: ignore[type-arg]
    import asyncio as _asyncio
    import logging as _logging
    # Change working directory to project root so relative paths (configs/, runs/, journal/) work
    project_root = Path(__file__).parent.parent.parent
    os.chdir(project_root)

    # Auto-restart auto scheduler if it was running before last shutdown
    from apps.web.paper_trading_service import auto_scheduler, load_saved_state
    _db_env = os.environ.get("PAPER_TRADER_DB", "paper_trading.db")
    _db_path = str(project_root / _db_env) if not Path(_db_env).is_absolute() else _db_env
    saved = load_saved_state(_db_path)
    if saved and saved.get("running"):
        try:
            auto_scheduler.start(
                sessions=saved.get("sessions", []),
                dry_run=saved.get("dry_run", False),
                db_path=saved.get("db_path") or _db_path,
            )
            _logging.getLogger(__name__).info(
                "Auto scheduler resumed from saved state (sessions=%s, dry_run=%s)",
                saved.get("sessions"), saved.get("dry_run"),
            )
        except Exception as exc:
            _logging.getLogger(__name__).warning("Auto scheduler auto-restart failed: %s", exc)

    yield

    # Graceful shutdown: cancel task but keep state so next restart can resume
    if auto_scheduler.running:
        auto_scheduler.shutdown()
        await _asyncio.sleep(1)


def create_app() -> FastAPI:
    app = FastAPI(
        title="Fithia2 Strategy Dashboard",
        description="Web GUI for ACE-F trading system — strategy management, backtesting, paper trading",
        version="1.0.0",
        lifespan=lifespan,
    )

    app.add_middleware(
        CORSMiddleware,
        allow_origins=["http://localhost:5173", "http://localhost:8000"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

    # API routers
    api_prefix = "/api"
    app.include_router(experiments.router, prefix=api_prefix)
    app.include_router(leaderboard.router, prefix=api_prefix)
    app.include_router(runs.router, prefix=api_prefix)
    app.include_router(backtest.router, prefix=api_prefix)
    app.include_router(paper_trading.router, prefix=api_prefix)
    app.include_router(sqs.router, prefix=api_prefix)
    app.include_router(docs.router, prefix=api_prefix)

    # Serve built frontend (production)
    if _STATIC_DIR.exists():
        app.mount("/assets", StaticFiles(directory=_STATIC_DIR / "assets"), name="assets")

        @app.get("/{full_path:path}", include_in_schema=False)
        async def serve_spa(full_path: str) -> FileResponse:
            # Serve index.html for all non-API routes (client-side routing)
            index = _STATIC_DIR / "index.html"
            if index.exists():
                return FileResponse(index)
            return FileResponse(_STATIC_DIR / "index.html")

    return app


app = create_app()


def run_server() -> None:
    """Start the web server. Called by `fithia2 web`."""
    import uvicorn

    from apps.web.config import settings

    uvicorn.run(
        "apps.web.main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.reload,
        log_level="info",
        loop="asyncio",
    )


if __name__ == "__main__":
    run_server()
