"""Integration test for wait_until in on_boot automation.

This test validates that wait_until works correctly when triggered from on_boot,
which runs at the same setup priority as WaitUntilAction itself. This was broken
before the fix because WaitUntilAction::setup() would unconditionally disable_loop(),
even if play_complex() had already been called and enabled the loop.

The bug: on_boot fires during StartupTrigger::setup(), which calls WaitUntilAction::play_complex()
before WaitUntilAction::setup() has run. Then when WaitUntilAction::setup() runs, it calls
disable_loop(), undoing the enable_loop() from play_complex(), causing wait_until to hang forever.
"""

from __future__ import annotations

import asyncio
import re

import pytest

from .types import APIClientConnectedFactory, RunCompiledFunction


@pytest.mark.asyncio
async def test_wait_until_on_boot(
    yaml_config: str,
    run_compiled: RunCompiledFunction,
    api_client_connected: APIClientConnectedFactory,
) -> None:
    """Test that wait_until works in on_boot automation with a condition that becomes true later."""
    loop = asyncio.get_running_loop()

    on_boot_started = False
    on_boot_completed = False

    on_boot_started_pattern = re.compile(r"on_boot: Starting wait_until test")
    on_boot_complete_pattern = re.compile(r"on_boot: wait_until completed successfully")

    on_boot_started_future = loop.create_future()
    on_boot_complete_future = loop.create_future()

    def check_output(line: str) -> None:
        """Check log output for test progress."""
        nonlocal on_boot_started, on_boot_completed

        if on_boot_started_pattern.search(line):
            on_boot_started = True
            if not on_boot_started_future.done():
                on_boot_started_future.set_result(True)

        if on_boot_complete_pattern.search(line):
            on_boot_completed = True
            if not on_boot_complete_future.done():
                on_boot_complete_future.set_result(True)

    async with (
        run_compiled(yaml_config, line_callback=check_output),
        api_client_connected() as client,
    ):
        # Wait for on_boot to start
        await asyncio.wait_for(on_boot_started_future, timeout=10.0)
        assert on_boot_started, "on_boot did not start"

        # At this point, on_boot is blocked in wait_until waiting for test_flag to become true
        # If the bug exists, wait_until's loop is disabled and it will never complete
        # even after we set the flag

        # Give a moment for setup to complete
        await asyncio.sleep(0.5)

        # Now set the flag that wait_until is waiting for
        _, services = await client.list_entities_services()
        set_flag_service = next(
            (s for s in services if s.name == "set_test_flag"), None
        )
        assert set_flag_service is not None, "set_test_flag service not found"

        await client.execute_service(set_flag_service, {})

        # If the fix works, wait_until's loop() will check the condition and proceed
        # If the bug exists, wait_until is stuck with disabled loop and will timeout
        try:
            await asyncio.wait_for(on_boot_complete_future, timeout=2.0)
            assert on_boot_completed, (
                "on_boot wait_until did not complete after flag was set"
            )
        except TimeoutError:
            pytest.fail(
                "wait_until in on_boot did not complete within 2s after condition became true. "
                "This indicates the bug where WaitUntilAction::setup() disables the loop "
                "after play_complex() has already enabled it."
            )
