"""Shared asyncio loop lifecycle helpers for the desktop application.

The GUI runs on Tk's main thread, while database calls are executed on a
dedicated background event loop. These helpers expose that loop as a lazy
singleton so every module can schedule work on the same async runtime.
"""

import asyncio
import threading
import contextlib

from version_info import module_version

__version__ = module_version(__name__)


class _LoopHolder:
    """Store the global loop instance and its worker thread."""

    def __init__(self):
        self.loop = None
        self.thread = None
        self.closing = False


_GLOBAL = _LoopHolder()


def get_global_loop() -> asyncio.AbstractEventLoop:
    """Return the shared background event loop.

    The loop is created lazily the first time the function is called and kept
    alive for the lifetime of the application.
    """
    if _GLOBAL.loop and not _GLOBAL.closing:
        return _GLOBAL.loop

    ready = threading.Event()

    def _run():
        _GLOBAL.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(_GLOBAL.loop)
        ready.set()
        try:
            _GLOBAL.loop.run_forever()
        finally:
            loop = _GLOBAL.loop
            if loop is not None:
                pending = [task for task in asyncio.all_tasks(loop) if not task.done()]
                for task in pending:
                    task.cancel()
                if pending:
                    with contextlib.suppress(Exception):
                        loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
                with contextlib.suppress(Exception):
                    loop.run_until_complete(loop.shutdown_asyncgens())
                with contextlib.suppress(Exception):
                    loop.run_until_complete(loop.shutdown_default_executor())
                with contextlib.suppress(Exception):
                    loop.close()

    _GLOBAL.thread = threading.Thread(target=_run, name="asyncio-bg-loop", daemon=True)
    _GLOBAL.thread.start()
    ready.wait()
    return _GLOBAL.loop


def stop_global_loop():
    """Stop the shared loop and join the background thread if present."""
    if _GLOBAL.loop and _GLOBAL.loop.is_running():
        _GLOBAL.closing = True
        _GLOBAL.loop.call_soon_threadsafe(_GLOBAL.loop.stop)
        _GLOBAL.thread.join(timeout=2)
        _GLOBAL.loop = None
        _GLOBAL.thread = None
        _GLOBAL.closing = False
