"""
VoxCast - Entry point.
Sets up logging, CLI arguments, and launches the Qt+asyncio event loop.
"""
from __future__ import annotations

import argparse
import asyncio
import sys
from pathlib import Path

from paths import data_dir, app_dir
from version import __version__

APP_MUTEX_NAME = "Global\\VoxCastSingleInstance"


def _ensure_single_instance() -> object:
    """
    Create a named Windows mutex. Returns the mutex handle (must be kept alive).
    Exits the process if another instance is already running.
    """
    import ctypes
    import ctypes.wintypes

    kernel32 = ctypes.windll.kernel32  # type: ignore[attr-defined]
    handle = kernel32.CreateMutexW(None, True, APP_MUTEX_NAME)
    err = kernel32.GetLastError()

    if err == 183:  # ERROR_ALREADY_EXISTS
        # Bring existing window to front by posting a message via FindWindow
        hwnd = ctypes.windll.user32.FindWindowW(None, "VoxCast")  # type: ignore[attr-defined]
        if hwnd:
            ctypes.windll.user32.ShowWindow(hwnd, 9)   # SW_RESTORE  # type: ignore[attr-defined]
            ctypes.windll.user32.SetForegroundWindow(hwnd)  # type: ignore[attr-defined]
        sys.exit(0)

    return handle  # keep alive for process lifetime


_crash_log_handle = None  # keep the file open for the process lifetime


def _setup_crash_handler(log_dir: Path) -> None:
    """Write native-crash stacks (access violations in Qt/CUDA/PortAudio) to
    logs/crash.log. Without this, hard crashes leave no trace at all."""
    global _crash_log_handle
    import faulthandler
    from loguru import logger

    try:
        crash_log = log_dir / "crash.log"
        if crash_log.exists() and crash_log.stat().st_size > 0:
            logger.warning("Previous native crash recorded in {}", crash_log)
            # Cap growth across many crashes; keep the most recent ones
            if crash_log.stat().st_size > 512_000:
                crash_log.unlink()
        _crash_log_handle = open(crash_log, "a", encoding="utf-8")
        faulthandler.enable(file=_crash_log_handle)
    except Exception as e:
        logger.warning("Crash handler setup failed: {}", e)


def _setup_logging(debug: bool = False) -> None:
    from loguru import logger

    log_dir = data_dir() / "logs"
    log_dir.mkdir(parents=True, exist_ok=True)

    logger.remove()

    level = "DEBUG" if debug else "INFO"
    logger.add(
        sys.stderr,
        level=level,
        format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | {message}",
        colorize=True,
    )
    logger.add(
        log_dir / "voxcast.log",
        level="DEBUG",
        rotation="10 MB",
        retention=3,
        encoding="utf-8",
        format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{line} | {message}",
    )


def _parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        prog="VoxCast",
        description="VoxCast - Lokale Spracherkennung",
    )
    parser.add_argument(
        "--version",
        action="version",
        version=f"VoxCast {__version__}",
    )
    parser.add_argument(
        "--debug",
        action="store_true",
        help="Enable debug logging",
    )
    parser.add_argument(
        "--convert",
        metavar="PATH",
        default=None,
        help="Convert a file to Markdown (forwarded to a running instance if any)",
    )
    parser.add_argument(
        "--register-shell",
        action="store_true",
        help="Register the 'Convert to Markdown' right-click context menu and exit",
    )
    parser.add_argument(
        "--unregister-shell",
        action="store_true",
        help="Remove the 'Convert to Markdown' right-click context menu and exit",
    )
    return parser.parse_args()


async def _main(args: argparse.Namespace) -> None:
    from loguru import logger

    logger.info("VoxCast {} starting...", __version__)
    logger.info("Python {}", sys.version.split()[0])
    logger.info("Working directory: {}", Path.cwd())

    from app import VoxCastApp

    app = VoxCastApp()
    if getattr(args, "convert", None):
        app.set_pending_convert(args.convert)

    await app.run()


def _handle_shell_commands(args: argparse.Namespace) -> bool:
    """Handle --register-shell / --unregister-shell. Returns True if handled."""
    if not (args.register_shell or args.unregister_shell):
        return False
    from loguru import logger
    from utils import shell_integration
    try:
        if args.register_shell:
            n = shell_integration.register()
            logger.info("Registered context menu for {} extensions", n)
        else:
            n = shell_integration.unregister()
            logger.info("Removed context menu ({} keys)", n)
    except Exception as e:
        logger.error("Shell integration command failed: {}", e)
        sys.exit(1)
    return True


def main() -> None:
    args = _parse_args()

    _setup_logging(debug=args.debug)
    _setup_crash_handler(data_dir() / "logs")

    # Shell-integration register/unregister run headless and exit.
    if _handle_shell_commands(args):
        sys.exit(0)

    # If asked to convert and an instance is already running, forward the
    # request via IPC and exit — the warm instance does the work.
    if getattr(args, "convert", None):
        from loguru import logger
        from utils.ipc import send_convert_request
        if send_convert_request(args.convert):
            logger.info("Convert request forwarded; exiting launcher")
            sys.exit(0)
        # else: no running instance — fall through to start one (pending convert)

    # Prevent multiple instances
    _mutex = _ensure_single_instance()  # noqa: F841 (keep alive)

    # Use lightweight Basic style instead of Material (much faster rendering)
    import os
    os.environ.setdefault("QT_QUICK_CONTROLS_STYLE", "Basic")

    # Set Windows App User Model ID BEFORE QApplication — controls notification title
    import ctypes
    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("VoxCast")  # type: ignore[attr-defined]

    # QApplication MUST exist before asyncio / QtAsyncio event loop is created
    from PySide6.QtWidgets import QApplication
    qt_app = QApplication(sys.argv)
    qt_app.setQuitOnLastWindowClosed(False)
    qt_app.setApplicationName("VoxCast")
    qt_app.setApplicationVersion(__version__)

    from PySide6.QtGui import QIcon
    _logo = app_dir() / "assets" / "logo.svg"
    if _logo.exists():
        qt_app.setWindowIcon(QIcon(str(_logo)))

    from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
    asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
    try:
        asyncio.run(_main(args))
    except RuntimeError as e:
        # QtAsyncio raises benign RuntimeErrors during event loop cleanup on exit
        if "Event loop" not in str(e):
            raise


if __name__ == "__main__":
    main()
