# kybernis/_asyncutil.py
from __future__ import annotations
import asyncio
import threading
from typing import Callable, Any

def fire_and_forget(fn: Callable[[], Any]) -> None:
    """
    Run fn ASAP without blocking caller.
    - If we're in an event loop: schedule a task
    - Else: run in a daemon thread
    """
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        def _wrapped():
            try:
                fn()
            except Exception as e:
                print(f"[kybernis] fire_and_forget thread error: {e}", flush=True)

        # Use non-daemon thread for debugging so events aren't lost on process exit
        t = threading.Thread(target=_wrapped, daemon=False)
        t.start()
        return

    async def _runner():
        try:
            await asyncio.to_thread(lambda: fn())
        except Exception as e:
            print(f"[kybernis] fire_and_forget async error: {e}", flush=True)

    loop.create_task(_runner())
