import asyncio

from androidtvremote2 import (
    AndroidTVRemote,
    CannotConnect,
    ConnectionClosed,
    InvalidAuth,
)

from gtv.constants import CERT_DIR, CERT_FILE, CLIENT_NAME, KEY_FILE


def ensure_cert_dir():
    CERT_DIR.mkdir(parents=True, exist_ok=True)


def run_async(coro):
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = None

    if loop and loop.is_running():
        new_loop = asyncio.new_event_loop()
        try:
            return new_loop.run_until_complete(coro)
        finally:
            new_loop.close()
    else:
        return asyncio.run(coro)


async def async_pair(host: str, pout) -> bool:
    ensure_cert_dir()
    remote = AndroidTVRemote(CLIENT_NAME, str(CERT_FILE), str(KEY_FILE), host)

    if await remote.async_generate_cert_if_missing():
        pout("Generated new client certificate.")

    try:
        name, mac = await remote.async_get_name_and_mac()
        pout(f"Device: {name}")
        pout(f"MAC:    {mac}")
        pout(f"Host:   {host}")
    except (CannotConnect, ConnectionClosed, OSError) as e:
        pout(f"Error: Could not reach device at {host}: {e}")
        return False

    confirm = input("Start pairing? A PIN will appear on your TV. [Y/n]: ").strip().lower()
    if confirm and confirm != "y":
        pout("Pairing cancelled.")
        return False

    try:
        await remote.async_start_pairing()
    except (CannotConnect, ConnectionClosed, OSError) as e:
        pout(f"Error starting pairing: {e}")
        return False

    while True:
        code = input("Enter the pairing code shown on your TV: ").strip()
        if not code:
            continue
        try:
            await remote.async_finish_pairing(code)
            pout("Pairing successful!")
            return True
        except InvalidAuth:
            pout("Invalid code. Try again.")
        except ConnectionClosed:
            pout("Connection lost. Restarting pairing...")
            try:
                await remote.async_start_pairing()
            except (CannotConnect, ConnectionClosed, OSError) as e:
                pout(f"Error: {e}")
                return False
