import sys
from pathlib import Path
import argparse

# Ajouter le chemin racine du projet
sys.path.append(str(Path(__file__).resolve().parent.parent))

import os
import importlib
from fastapi import FastAPI, APIRouter
from contextlib import asynccontextmanager
import logging
from collections.abc import Awaitable, Callable
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from starlette.requests import Request
from starlette.responses import Response
from app.config import is_running_in_docker
from app.services import videoStreamerApp
from app.services import RequestProcessor
try:
    # Works when running as a package: `python -m app.main`
    from .config import ServerConfiguration
except ImportError:
    # Works when running as a script: `python app/main.py`
    from config import ServerConfiguration


MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.getenv('CONFIG_FILENAME', os.path.join(MODULE_DIR, "server-config.json"))
ENV_DATABASE_NAME = "SYNCVIS_DATABASE_NAME"

def load_config():
    with open(CONFIG_FILE, "r", encoding="utf-8") as f:
        txt = f.read()
        config = ServerConfiguration.model_validate_json(txt)
        if is_running_in_docker():
            # Le mode reload peut provoquer des comportements indésirables en conteneur.
            config.server.reload = False        
        return config

config:ServerConfiguration = load_config()

def setup_logger() -> logging.Logger:
    # Configure un logger de namespace `app` pour capturer tous les modules app.*.
    level = getattr(logging, config.logging.level.upper(), logging.INFO)

    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter(config.logging.format, datefmt=config.logging.datefmt))

    package_logger = logging.getLogger("app")
    package_logger.handlers.clear()
    package_logger.propagate = False
    package_logger.setLevel(level)
    package_logger.addHandler(handler)

    logging.getLogger("httpx").setLevel(logging.WARNING)
    logging.getLogger("httpcore").setLevel(logging.WARNING)
    return logging.getLogger("app.main")

logger = setup_logger()

async def onApplicationStartup():
    global config
    # Code à exécuter au démarrage de l'application
    logger.info(f"Initialisation de l'application ...")
    logger.info(f"Démarrage du service de streaming vidéo...")
    await videoStreamerApp.init(config)
    await videoStreamerApp.start()
    logger.info(f"Initialisation de l'application terminée")
    
async def onApplicationShutdown():
    # Code à exécuter à l'arrêt de l'application
    logger.info(f"Arrêt de l'application...")
    logger.info(f"Arrêt du service de streaming vidéo...")
    await videoStreamerApp.stop()
    logger.info(f"Arrêt de l'application terminé")

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Code à exécuter au démarrage de l'application
    try:
        await onApplicationStartup()
    except Exception as e:
        # Afficher la pile d'erreur complète pour faciliter le débogage
        logger.exception("Une erreur s'est produite lors du démarrage de l'application :")
        raise e
    yield
    # Code à exécuter à l'arrêt de l'application
    await onApplicationShutdown()

app = FastAPI(lifespan=lifespan)
app.state.config = config
app.state.request_processor = RequestProcessor(config)

# Configuration CORS
if True:
    app.add_middleware(
        CORSMiddleware,
        
        # allow_origins=config.cors.allow_origins,  # Liste des origines autorisées (ex: ["http://localhost:8000"])

        allow_origin_regex=".*",
        
        allow_credentials=config.cors.allow_credentials,  # Permet les cookies et les autorisations
        allow_methods=config.cors.allow_methods,  # Permet toutes les méthodes
        allow_headers=config.cors.allow_headers,  # Permet tous les en-têtes
    )

# Compression des réponses
if True:
    app.add_middleware(GZipMiddleware, 
        minimum_size=1000, # Taille minimale des réponses à compresser
        compresslevel=9 # Niveau de compression
    )

if False:
    @app.middleware("http")
    async def debug_requests(request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response:
        # Log uniquement si c'est une tentative de connexion websocket ou api
        print(f"DEBUG: {request.method} {request.url}")
        print(f"DEBUG: Headers: {dict(request.headers)}")
        return await call_next(request)

# Chemin vers le dossier `routes`
API_ROUTES_DIR = os.path.join(MODULE_DIR, "routes")

# Scanner tous les fichiers Python dans `routes`
for file in os.listdir(API_ROUTES_DIR):
    if file.endswith(".py") and file != "__init__.py":
        module_name = f"app.routes.{file[:-3]}"  # Retirer `.py` pour obtenir le nom du module
        module = importlib.import_module(module_name)
        # Vérifier si le fichier contient un router et l'inclure
        if hasattr(module, "router") and isinstance(module.router, APIRouter):
            if __name__ == "app.main":
                logger.info(f"Inclusion du router depuis {module_name}")
            app.include_router(module.router, prefix=config.api.prefix, tags=config.api.tags)
        else:
            if __name__ == "app.main":
                logger.warning(f"Aucun router trouvé dans {module_name}, ce fichier sera ignoré.")


def parse_cli_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Lance le serveur SyncVis backend")
    parser.add_argument("--host", type=str, help="Adresse d'ecoute du serveur")
    parser.add_argument("--port", type=int, help="Port d'ecoute du serveur")
    parser.add_argument("--log-level", type=str, dest="log_level", help="Niveau de log Uvicorn")
    parser.add_argument("--ssl-keyfile", type=str, dest="ssl_keyfile", help="Chemin vers la cle SSL")
    parser.add_argument("--ssl-certfile", type=str, dest="ssl_certfile", help="Chemin vers le certificat SSL")
    parser.add_argument(
        "--reload",
        action=argparse.BooleanOptionalAction,
        default=None,
        help="Active/desactive le rechargement automatique",
    )
    return parser.parse_args()


def apply_cli_overrides(args: argparse.Namespace) -> None:
    global config
    if args.host is not None:
        config.server.host = args.host
    if args.port is not None:
        config.server.port = args.port
    if args.log_level is not None:
        config.server.log_level = args.log_level
    if args.ssl_keyfile is not None:
        config.server.ssl_keyfile = args.ssl_keyfile
    if args.ssl_certfile is not None:
        config.server.ssl_certfile = args.ssl_certfile
    if args.reload is not None:
        config.server.reload = args.reload

def main(args: argparse.Namespace | None = None) -> None:
    global config
    # Charger la configuration
    if config:
        if args is not None:
            apply_cli_overrides(args)
        # Démarrer le serveur FastAPI
        import uvicorn
        logger.info(f"Démarrage du serveur sur {config.server.host}:{config.server.port}")
        logger.info(f"Mode de rechargement : {config.server.reload}")
        if config.server.ssl_certfile and config.server.ssl_keyfile:
            logger.info(f"SSL activé avec certfile : {config.server.ssl_certfile} et keyfile : {config.server.ssl_keyfile}")
            uvicorn.run(
                app="app.main:app",
                host=config.server.host,
                port=config.server.port,
                log_level=config.server.log_level,
                # ws="wsproto",
                reload=config.server.reload,
                ssl_keyfile=config.server.ssl_keyfile,
                ssl_certfile=config.server.ssl_certfile
            )
        else:
            logger.info("SSL non configuré, démarrage en HTTP")
            uvicorn.run(
                app="app.main:app",
                host=config.server.host,
                port=config.server.port,
                log_level=config.server.log_level,
                # ws="wsproto",
                reload=config.server.reload
            )
    else:
        logger.error("Impossible de démarrer l'application sans configuration valide.")

if __name__ == "__main__":
    cli_args = parse_cli_args()
    main(cli_args)
