"""Haupt-Server: HTTP + WebSocket + Templates in einer aiohttp-App"""
import asyncio
import logging
import os
import time
from pathlib import Path
from aiohttp import web
import aiohttp_jinja2
import jinja2
from app.config import Config
from app.services.queue import QueueService
from app.services.scanner import ScannerService
from app.services.encoder import EncoderService
from app.routes.ws import WebSocketManager
from app.services.library import LibraryService
from app.services.tvdb import TVDBService
from app.services.cleaner import CleanerService
from app.services.importer import ImporterService
from app.services.auth import AuthService
from app.services.hls import HLSSessionManager
from app.services.i18n import load_translations, setup_jinja2_i18n
from app.routes.api import setup_api_routes
from app.routes.library_api import setup_library_routes
from app.routes.pages import setup_page_routes
from app.routes.tv_api import setup_tv_routes


class VideoKonverterServer:
    """Haupt-Server - ein Port fuer HTTP, WebSocket und Admin-UI"""

    def __init__(self):
        self.config = Config()
        self.config.setup_logging()

        # Services
        self.ws_manager = WebSocketManager()
        self.scanner = ScannerService(self.config)
        self.queue_service = QueueService(self.config, self.ws_manager)
        self.ws_manager.set_queue_service(self.queue_service)

        # Bibliothek-Services
        self.library_service = LibraryService(self.config, self.ws_manager)
        self.tvdb_service = TVDBService(self.config)
        self.cleaner_service = CleanerService(self.config, self.library_service)
        self.importer_service = ImporterService(
            self.config, self.library_service, self.tvdb_service
        )

        # aiohttp App (50 GiB Upload-Limit fuer grosse Videodateien)
        self.app = web.Application(
            client_max_size=50 * 1024 * 1024 * 1024,
            middlewares=[self._no_cache_middleware]
        )
        self._setup_app()

    @web.middleware
    async def _no_cache_middleware(self, request: web.Request,
                                   handler) -> web.Response:
        """Verhindert Browser-Caching fuer API-Responses + Error-Logging"""
        try:
            response = await handler(request)
        except web.HTTPException as he:
            if he.status >= 500:
                logging.error(f"HTTP {he.status} bei {request.method} {request.path}: {he.reason}",
                              exc_info=True)
            raise
        except Exception as e:
            logging.error(f"Unbehandelte Ausnahme bei {request.method} {request.path}: {e}",
                          exc_info=True)
            raise
        if request.path.startswith("/api/"):
            # Thumbnail-Bilder sollen gecacht werden (Cache-Control vom Handler)
            if "/thumbnail" not in request.path:
                response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate"
                response.headers["Pragma"] = "no-cache"
                response.headers["Expires"] = "0"
        return response

    def _setup_app(self) -> None:
        """Konfiguriert die aiohttp-Application"""
        # Jinja2 Templates (request_processor macht request in Templates verfuegbar)
        template_dir = Path(__file__).parent / "templates"
        aiohttp_jinja2.setup(
            self.app,
            loader=jinja2.FileSystemLoader(str(template_dir)),
            context_processors=[aiohttp_jinja2.request_processor],
            bytecode_cache=jinja2.FileSystemBytecodeCache(
                directory="/tmp/jinja2_cache",
                pattern="__jinja2_%s.cache",
            ),
            auto_reload=os.environ.get("VK_DEV", "").lower() == "true",
        )

        # i18n: Uebersetzungen laden und Jinja2-Filter registrieren
        static_dir = Path(__file__).parent / "static"
        load_translations(str(static_dir))
        setup_jinja2_i18n(self.app)

        # Cache-Busting: Versionsstempel fuer statische Dateien
        env = self.app[aiohttp_jinja2.APP_KEY]
        env.globals["v"] = str(int(time.time()))

        # WebSocket Route
        ws_path = self.config.server_config.get("websocket_path", "/ws")
        self.app.router.add_get(ws_path, self.ws_manager.handle_websocket)

        # API Routes
        setup_api_routes(
            self.app, self.config, self.queue_service, self.scanner,
            self.ws_manager
        )

        # Bibliothek API Routes
        setup_library_routes(
            self.app, self.config, self.library_service,
            self.tvdb_service, self.queue_service,
            self.cleaner_service, self.importer_service,
        )

        # Seiten Routes
        setup_page_routes(self.app, self.config, self.queue_service)

        # TV-App Routes (Auth-Service, DB-Pool wird in on_startup gesetzt)
        async def _lazy_pool():
            return self.library_service._db_pool
        self.auth_service = AuthService(_lazy_pool)
        self.hls_manager = HLSSessionManager(
            self.library_service, self.config.gpu_device,
            queue_service=self.queue_service, config=self.config)
        setup_tv_routes(
            self.app, self.config,
            self.auth_service, self.library_service,
            self.hls_manager,
        )

        # Statische Dateien
        static_dir = Path(__file__).parent / "static"
        if static_dir.exists():
            self.app.router.add_static(
                "/static/", path=str(static_dir), name="static"
            )

        # Startup/Shutdown Hooks
        self.app.on_startup.append(self._on_startup)
        self.app.on_shutdown.append(self._on_shutdown)

    async def _on_startup(self, app: web.Application) -> None:
        """Server-Start: GPU pruefen, Queue starten"""
        mode = self.config.encoding_mode

        # Auto-Detection
        if mode == "auto":
            gpu_ok = EncoderService.detect_gpu_available()
            if gpu_ok:
                gpu_ok = await EncoderService.test_gpu_encoding(
                    self.config.gpu_device
                )
            if gpu_ok:
                self.config.settings["encoding"]["mode"] = "gpu"
                self.config.settings["encoding"]["default_preset"] = "gpu_av1"
                logging.info(f"GPU erkannt ({self.config.gpu_device}), "
                             f"verwende GPU-Encoding")
            else:
                self.config.settings["encoding"]["mode"] = "cpu"
                self.config.settings["encoding"]["default_preset"] = "cpu_av1"
                logging.info("Keine GPU erkannt, verwende CPU-Encoding")
        else:
            logging.info(f"Encoding-Modus: {mode}")

        # Queue starten
        await self.queue_service.start()

        # Bibliothek starten
        await self.library_service.start()

        # DB-Pool mit anderen Services teilen
        if self.library_service._db_pool:
            self.tvdb_service.set_db_pool(self.library_service._db_pool)
            self.importer_service.set_db_pool(self.library_service._db_pool)

        # WebSocket-Manager an ImporterService fuer Live-Updates
        self.importer_service.set_ws_manager(self.ws_manager)

        # Zusaetzliche DB-Tabellen erstellen
        await self.tvdb_service.init_db()
        await self.importer_service.init_db()

        # TV-App Auth-Service: DB-Tabellen initialisieren (Pool kommt ueber lazy getter)
        if self.library_service._db_pool:
            await self.auth_service.init_db()

        # HLS Session Manager starten
        await self.hls_manager.start()

        host = self.config.server_config.get("host", "0.0.0.0")
        port = self.config.server_config.get("port", 8080)
        logging.info(f"Server bereit auf http://{host}:{port}")

    async def _on_shutdown(self, app: web.Application) -> None:
        """Server-Stop: Queue und Library stoppen"""
        await self.hls_manager.stop()
        await self.queue_service.stop()
        await self.library_service.stop()
        logging.info("Server heruntergefahren")

    async def run(self) -> None:
        """Startet den Server"""
        host = self.config.server_config.get("host", "0.0.0.0")
        port = self.config.server_config.get("port", 8080)

        runner = web.AppRunner(self.app)
        await runner.setup()
        site = web.TCPSite(runner, host, port)
        await site.start()

        logging.info(
            f"VideoKonverter Server laeuft auf http://{host}:{port}\n"
            f"  Dashboard:  http://{host}:{port}/\n"
            f"  Bibliothek: http://{host}:{port}/library\n"
            f"  Admin:      http://{host}:{port}/admin\n"
            f"  Statistik:  http://{host}:{port}/statistics\n"
            f"  TV-App:     http://{host}:{port}/tv/\n"
            f"  WebSocket:  ws://{host}:{port}/ws\n"
            f"  API:        http://{host}:{port}/api/convert (POST)"
        )

        # Endlos laufen bis Interrupt
        await asyncio.Event().wait()
