from __future__ import annotations

import logging
import os
import signal
import sys

from fastapi import FastAPI, Request

from sitedrop.server.config import DEFAULT_CONFIG_DIR, ServerConfig
from sitedrop.server.routes_api import router as api_router
from sitedrop.server.routes_sites import router as sites_router
from sitedrop.server.storage import SiteStorage

LOG_FILE = DEFAULT_CONFIG_DIR / "server.log"
LOG_BACKUP = DEFAULT_CONFIG_DIR / "server.log.1"
LOG_MAX_BYTES = 5 * 1024 * 1024


def _setup_logging() -> None:
    logger = logging.getLogger("sitedrop")
    if not logger.handlers:
        handler = logging.StreamHandler()
        handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
        logger.addHandler(handler)
        logger.setLevel(logging.INFO)


def _handle_sighup(signum, frame):
    try:
        if LOG_FILE.exists() and LOG_FILE.stat().st_size > LOG_MAX_BYTES:
            LOG_FILE.replace(LOG_BACKUP)
        fd = os.open(str(LOG_FILE), os.O_WRONLY | os.O_CREAT | os.O_APPEND)
        os.dup2(fd, sys.stdout.fileno())
        os.dup2(fd, sys.stderr.fileno())
        os.close(fd)
    except OSError:
        pass


def _register_sighup_handler() -> None:
    if sys.platform != "win32":
        signal.signal(signal.SIGHUP, _handle_sighup)


def create_app(config: ServerConfig | None = None) -> FastAPI:
    if config is None:
        config = ServerConfig.load()

    _setup_logging()
    _register_sighup_handler()

    app = FastAPI(title="sitedrop", docs_url=None, redoc_url=None)
    app.state.config = config
    app.state.storage = SiteStorage(config.sites_dir)

    @app.middleware("http")
    async def security_headers(request: Request, call_next):
        response = await call_next(request)
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        if request.url.path.startswith("/api/"):
            response.headers["Content-Security-Policy"] = (
                "default-src 'none'; frame-ancestors 'none'"
            )
        return response

    # API router first so /api/* isn't caught by the site catch-all
    app.include_router(api_router)
    app.include_router(sites_router)

    return app
