"""Optimized dashboard app with improved async handling and non-blocking startup.

Key improvements:
1. Background tasks use asyncio.create_task() to avoid blocking startup
2. Chat integrations start in background
3. All startup operations complete quickly
4. Security and logging handled by dedicated middleware
"""

import logging
import re
from pathlib import Path

from fastapi import FastAPI, Request, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles

from config import settings

# Import dedicated middleware
from dashboard.middleware.csrf import CSRFMiddleware
from dashboard.middleware.rate_limit import RateLimitMiddleware
from dashboard.middleware.request_logging import RequestLoggingMiddleware
from dashboard.middleware.security_headers import SecurityHeadersMiddleware
from dashboard.routes.agents import router as agents_router
from dashboard.routes.briefing import router as briefing_router
from dashboard.routes.calm import router as calm_router
from dashboard.routes.chat_api import router as chat_api_router
from dashboard.routes.chat_api_v1 import router as chat_api_v1_router
from dashboard.routes.daily_run import router as daily_run_router
from dashboard.routes.db_explorer import router as db_explorer_router
from dashboard.routes.discord import router as discord_router
from dashboard.routes.energy import router as energy_router
from dashboard.routes.experiments import router as experiments_router
from dashboard.routes.grok import router as grok_router
from dashboard.routes.health import router as health_router
from dashboard.routes.hermes import router as hermes_router
from dashboard.routes.legal import router as legal_router
from dashboard.routes.loop_qa import router as loop_qa_router
from dashboard.routes.memory import router as memory_router
from dashboard.routes.mobile import router as mobile_router
from dashboard.routes.models import api_router as models_api_router
from dashboard.routes.models import router as models_router
from dashboard.routes.monitoring import router as monitoring_router
from dashboard.routes.nexus import router as nexus_router
from dashboard.routes.quests import router as quests_router
from dashboard.routes.scorecards import router as scorecards_router
from dashboard.routes.self_correction import router as self_correction_router
from dashboard.routes.seo import router as seo_router
from dashboard.routes.sovereignty_metrics import router as sovereignty_metrics_router
from dashboard.routes.sovereignty_ws import router as sovereignty_ws_router
from dashboard.routes.spark import router as spark_router
from dashboard.routes.system import router as system_router
from dashboard.routes.tasks import router as tasks_router
from dashboard.routes.telegram import router as telegram_router
from dashboard.routes.thinking import router as thinking_router
from dashboard.routes.three_strike import router as three_strike_router
from dashboard.routes.tools import router as tools_router
from dashboard.routes.tower import router as tower_router
from dashboard.routes.voice import router as voice_router
from dashboard.routes.work_orders import router as work_orders_router
from dashboard.routes.world import matrix_router
from dashboard.routes.world import router as world_router
from dashboard.schedulers import (  # noqa: F401 — re-export for backward compat
    _SYNTHESIZED_STATE,
    _presence_watcher,
)
from dashboard.startup import lifespan


class _ColorFormatter(logging.Formatter):
    """ANSI color formatter — red is reserved for ERROR/CRITICAL only."""

    RESET = "\033[0m"
    COLORS = {
        logging.DEBUG: "\033[37m",  # white/gray
        logging.INFO: "\033[32m",  # green
        logging.WARNING: "\033[33m",  # yellow
        logging.ERROR: "\033[31m",  # red
        logging.CRITICAL: "\033[1;31m",  # bold red
    }

    def format(self, record: logging.LogRecord) -> str:
        color = self.COLORS.get(record.levelno, self.RESET)
        formatted = super().format(record)
        return f"{color}{formatted}{self.RESET}"


def _configure_logging() -> None:
    """Configure logging with console and optional rotating file handler."""
    root_logger = logging.getLogger()
    root_logger.setLevel(logging.INFO)

    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    console.setFormatter(
        _ColorFormatter(
            "%(asctime)s %(levelname)-8s %(name)s — %(message)s",
            datefmt="%H:%M:%S",
        )
    )
    root_logger.addHandler(console)

    # Override uvicorn's default colored formatter so all console output
    # uses our color scheme (red = ERROR/CRITICAL only).
    for name in ("uvicorn", "uvicorn.error", "uvicorn.access"):
        uv_logger = logging.getLogger(name)
        uv_logger.handlers.clear()
        uv_logger.propagate = True

    if settings.error_log_enabled:
        from logging.handlers import RotatingFileHandler

        log_dir = Path(settings.repo_root) / settings.error_log_dir
        log_dir.mkdir(parents=True, exist_ok=True)
        error_file = log_dir / "errors.log"

        file_handler = RotatingFileHandler(
            error_file,
            maxBytes=settings.error_log_max_bytes,
            backupCount=settings.error_log_backup_count,
        )
        file_handler.setLevel(logging.ERROR)
        file_handler.setFormatter(
            logging.Formatter(
                "%(asctime)s %(levelname)-8s %(name)s — %(message)s\n"
                "  File: %(pathname)s:%(lineno)d\n"
                "  Function: %(funcName)s",
                datefmt="%Y-%m-%d %H:%M:%S",
            )
        )
        root_logger.addHandler(file_handler)


_configure_logging()
logger = logging.getLogger(__name__)

BASE_DIR = Path(__file__).parent
PROJECT_ROOT = BASE_DIR.parent.parent


app = FastAPI(
    title="Mission Control",
    version="1.0.0",
    lifespan=lifespan,
    docs_url="/docs",
    openapi_url="/openapi.json",
)


def _get_cors_origins() -> list[str]:
    """Get CORS origins from settings, rejecting wildcards in production.

    Adds matrix_frontend_url when configured. Always allows Tailscale IPs
    (100.x.x.x range) for development convenience.
    """
    origins = list(settings.cors_origins)

    # Strip wildcards in production (security)
    if "*" in origins and not settings.debug:
        logger.warning(
            "Wildcard '*' in CORS_ORIGINS stripped in production — "
            "set explicit origins via CORS_ORIGINS env var"
        )
        origins = [o for o in origins if o != "*"]

    # Add Matrix frontend URL if configured
    if settings.matrix_frontend_url:
        url = settings.matrix_frontend_url.strip()
        if url and url not in origins:
            origins.append(url)
            logger.debug("Added Matrix frontend to CORS: %s", url)

    return origins


# Pattern to match Tailscale IPs (100.x.x.x) for CORS origin regex
_TAILSCALE_IP_PATTERN = re.compile(r"^https?://100\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?$")


def _is_tailscale_origin(origin: str) -> bool:
    """Check if origin is a Tailscale IP (100.x.x.x range)."""
    return bool(_TAILSCALE_IP_PATTERN.match(origin))


# Add dedicated middleware in correct order
# 1. Logging (outermost to capture everything)
app.add_middleware(RequestLoggingMiddleware, skip_paths=["/health"])

# 2. Rate Limiting (before security to prevent abuse early)
app.add_middleware(
    RateLimitMiddleware,
    path_prefixes=["/api/matrix/"],
    requests_per_minute=30,
)

# 3. Security Headers
app.add_middleware(SecurityHeadersMiddleware, production=not settings.debug)

# 4. CSRF Protection
app.add_middleware(CSRFMiddleware)

# 4. Standard FastAPI middleware
# In development, allow all hosts (Tailscale IPs, MagicDNS, etc.)
_trusted = settings.trusted_hosts if settings.timmy_env == "production" else ["*"]
app.add_middleware(
    TrustedHostMiddleware,
    allowed_hosts=_trusted,
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=_get_cors_origins(),
    allow_origin_regex=r"https?://100\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?",
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allow_headers=["Content-Type", "Authorization"],
)

# Mount static files
static_dir = PROJECT_ROOT / "static"
if static_dir.exists():
    app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")

# Shared templates instance
from dashboard.templating import templates  # noqa: E402

# Include routers
app.include_router(seo_router)
app.include_router(health_router)
app.include_router(agents_router)
app.include_router(voice_router)
app.include_router(mobile_router)
app.include_router(briefing_router)
app.include_router(telegram_router)
app.include_router(tools_router)
app.include_router(spark_router)
app.include_router(discord_router)
app.include_router(memory_router)
app.include_router(nexus_router)
app.include_router(grok_router)
app.include_router(models_router)
app.include_router(models_api_router)
app.include_router(chat_api_router)
app.include_router(chat_api_v1_router)
app.include_router(thinking_router)
app.include_router(calm_router)
app.include_router(tasks_router)
app.include_router(work_orders_router)
app.include_router(loop_qa_router)
app.include_router(system_router)
app.include_router(monitoring_router)
app.include_router(experiments_router)
app.include_router(db_explorer_router)
app.include_router(world_router)
app.include_router(matrix_router)
app.include_router(tower_router)
app.include_router(daily_run_router)
app.include_router(hermes_router)
app.include_router(energy_router)
app.include_router(quests_router)
app.include_router(scorecards_router)
app.include_router(sovereignty_metrics_router)
app.include_router(sovereignty_ws_router)
app.include_router(three_strike_router)
app.include_router(self_correction_router)
app.include_router(legal_router)


@app.websocket("/ws")
async def ws_redirect(websocket: WebSocket):
    """Catch stale /ws connections and close cleanly.

    websockets 16.0 dropped the legacy ``transfer_data_task`` attribute,
    so calling ``websocket.close()`` after accept triggers an
    AttributeError.  Use the raw ASGI send instead.
    """
    await websocket.accept()
    try:
        await websocket.close(code=1008, reason="Deprecated endpoint")
    except AttributeError:
        # websockets >= 16.0 — close via raw ASGI message
        await websocket.send({"type": "websocket.close", "code": 1008})


@app.websocket("/swarm/live")
async def swarm_live(websocket: WebSocket):
    """Swarm live event stream via WebSocket."""
    from infrastructure.ws_manager.handler import ws_manager as ws_mgr

    await ws_mgr.connect(websocket)
    try:
        while True:
            # Keep connection alive; events are pushed via ws_mgr.broadcast()
            await websocket.receive_text()
    except Exception as exc:
        logger.debug("WebSocket disconnect error: %s", exc)
        ws_mgr.disconnect(websocket)


@app.get("/swarm/agents/sidebar", response_class=HTMLResponse)
async def swarm_agents_sidebar():
    """HTMX partial: list active swarm agents for the dashboard sidebar."""
    try:
        from config import settings

        agents_yaml = settings.agents_config
        agents = agents_yaml.get("agents", {})
        lines = []
        for name, cfg in agents.items():
            model = cfg.get("model", "default")
            lines.append(
                f'<div class="mc-agent-row">'
                f'<span class="mc-agent-name">{name}</span>'
                f'<span class="mc-agent-model">{model}</span>'
                f"</div>"
            )
        return "\n".join(lines) if lines else '<div class="mc-muted">No agents configured</div>'
    except Exception as exc:
        logger.debug("Agents sidebar error: %s", exc)
        return '<div class="mc-muted">Agents unavailable</div>'


@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
    """Serve the public landing page (homepage value proposition)."""
    return templates.TemplateResponse(request, "landing.html", {})


@app.get("/dashboard", response_class=HTMLResponse)
async def dashboard(request: Request):
    """Serve the main mission-control dashboard."""
    return templates.TemplateResponse(request, "index.html", {})


@app.get("/shortcuts/setup")
async def shortcuts_setup():
    """Siri Shortcuts setup guide."""
    from integrations.shortcuts.siri import get_setup_guide

    return get_setup_guide()
