"""
BC.Game Crash — Forensic Analysis System
Main application entry point.

This file coordinates:
    engine.py
    protocol.py
    crash.py
    analysis.py
    dashboard.py

The system is designed to:
- ingest captured/live Crash data
- decode the protocol
- reconstruct Crash rounds
- verify cryptographic data
- perform statistical analysis
- expose results through the dashboard

No betting or automated wagering functionality is implemented.
"""

from __future__ import annotations

import asyncio
import logging
import signal
import sys
from pathlib import Path

from engine import CrashEngine
from protocol import CrashProtocol
from crash import CrashProcessor
from analysis import AnalysisEngine
from dashboard import Dashboard


# ============================================================
# PATHS
# ============================================================

BASE_DIR = Path(__file__).resolve().parent
DATA_DIR = BASE_DIR / "data"

DATA_DIR.mkdir(parents=True, exist_ok=True)


# ============================================================
# LOGGING
# ============================================================

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
    handlers=[
        logging.StreamHandler(sys.stdout),
        logging.FileHandler(
            DATA_DIR / "system.log",
            encoding="utf-8"
        ),
    ],
)

logger = logging.getLogger("BCCrash")


# ============================================================
# APPLICATION
# ============================================================

class CrashSystem:
    """
    Central application coordinator.

    The individual modules perform the actual work.
    This class connects those modules together so that
    data flows through one controlled pipeline.
    """

    def __init__(self) -> None:
        logger.info("Initializing BC.Game Crash analysis system...")

        self.protocol = CrashProtocol(
            data_dir=DATA_DIR
        )

        self.crash = CrashProcessor(
            data_dir=DATA_DIR
        )

        self.analysis = AnalysisEngine(
            data_dir=DATA_DIR
        )

        self.dashboard = Dashboard(
            data_dir=DATA_DIR
        )

        self.engine = CrashEngine(
            protocol=self.protocol,
            on_message=self.handle_message,
            data_dir=DATA_DIR,
        )

        self.running = False

    # ========================================================
    # MESSAGE PIPELINE
    # ========================================================

    async def handle_message(self, raw_message) -> None:
        """
        Main data pipeline.

        Every incoming message follows:

            raw data
                ↓
            protocol decoder
                ↓
            Crash processor
                ↓
            analysis engine
                ↓
            dashboard
        """

        try:
            # ------------------------------------------------
            # 1. Decode the raw network message
            # ------------------------------------------------

            decoded = self.protocol.decode(raw_message)

            if decoded is None:
                return

            # ------------------------------------------------
            # 2. Process Crash-specific event
            # ------------------------------------------------

            event = self.crash.process(decoded)

            if event is None:
                return

            # ------------------------------------------------
            # 3. Send structured event to analysis engine
            # ------------------------------------------------

            result = self.analysis.process(event)

            # ------------------------------------------------
            # 4. Update dashboard
            # ------------------------------------------------

            if result is not None:
                self.dashboard.update(result)

        except Exception:
            logger.exception(
                "Error while processing incoming message"
            )

    # ========================================================
    # START
    # ========================================================

    async def start(self) -> None:
        """
        Start the complete system.
        """

        if self.running:
            return

        self.running = True

        logger.info("--------------------------------------------")
        logger.info("BC.Game Crash Analysis System")
        logger.info("--------------------------------------------")
        logger.info("Data directory: %s", DATA_DIR)
        logger.info("Initializing components...")

        try:
            # Prepare protocol layer
            self.protocol.initialize()

            # Prepare Crash processor
            self.crash.initialize()

            # Prepare analysis engine
            self.analysis.initialize()

            # Prepare dashboard
            self.dashboard.initialize()

            logger.info("All components initialized.")
            logger.info("Starting data engine...")

            # Start network/capture engine
            await self.engine.start()

        except asyncio.CancelledError:
            logger.info("System cancellation requested.")

        except Exception:
            logger.exception("Fatal application error.")

        finally:
            await self.stop()

    # ========================================================
    # STOP
    # ========================================================

    async def stop(self) -> None:
        """
        Shut the entire application down cleanly.
        """

        if not self.running:
            return

        self.running = False

        logger.info("Stopping BC.Game Crash analysis system...")

        try:
            await self.engine.stop()
        except Exception:
            logger.exception("Error stopping engine.")

        try:
            self.analysis.shutdown()
        except Exception:
            logger.exception("Error shutting down analysis engine.")

        try:
            self.dashboard.shutdown()
        except Exception:
            logger.exception("Error shutting down dashboard.")

        logger.info("System stopped.")


# ============================================================
# SIGNAL HANDLING
# ============================================================

def install_signal_handlers(loop, system: CrashSystem) -> None:
    """
    Allow CTRL+C / termination signals to shut down the
    application gracefully.
    """

    def request_shutdown() -> None:
        logger.info("Shutdown signal received.")

        task = asyncio.create_task(
            system.stop()
        )

        def finished(_task) -> None:
            loop.stop()

        task.add_done_callback(finished)

    for sig in (signal.SIGINT, signal.SIGTERM):
        try:
            loop.add_signal_handler(
                sig,
                request_shutdown
            )
        except (NotImplementedError, RuntimeError):
            # Windows/event-loop compatibility.
            pass


# ============================================================
# ENTRY POINT
# ============================================================

def main() -> None:
    """
    Application entry point.
    """

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    system = CrashSystem()

    install_signal_handlers(
        loop,
        system
    )

    try:
        loop.run_until_complete(
            system.start()
        )

    except KeyboardInterrupt:
        logger.info("Keyboard interruption received.")

    finally:
        try:
            pending = asyncio.all_tasks(loop)

            for task in pending:
                task.cancel()

            if pending:
                loop.run_until_complete(
                    asyncio.gather(
                        *pending,
                        return_exceptions=True
                    )
                )

        except Exception:
            logger.exception(
                "Error during final shutdown."
            )

        finally:
            loop.close()

    logger.info("Application exited.")


if __name__ == "__main__":
    main()