"""Integration test for script.wait FIFO ordering (issues #12043, #12044).

This test verifies that ScriptWaitAction processes queued items in FIFO order.

PR #7972 introduced bugs in ScriptWaitAction:
- Used emplace_front() causing LIFO ordering instead of FIFO
- Called loop() synchronously causing reentrancy issues
- Used while loop processing entire queue causing infinite loops

These bugs manifested as:
- Scripts becoming "zombies" (stuck in running state)
- script.wait hanging forever
- Incorrect execution order
"""

from __future__ import annotations

import asyncio
import re

import pytest

from .types import APIClientConnectedFactory, RunCompiledFunction


@pytest.mark.asyncio
async def test_script_delay_with_params(
    yaml_config: str,
    run_compiled: RunCompiledFunction,
    api_client_connected: APIClientConnectedFactory,
) -> None:
    """Test that script.wait processes queued items in FIFO order.

    This reproduces issues #12043 and #12044 where scripts would hang or become
    zombies due to LIFO ordering bugs in ScriptWaitAction from PR #7972.
    """
    test_complete = asyncio.Event()

    # Patterns to match in logs
    father_calling_pattern = re.compile(r"Father iteration (\d+): calling son")
    son_started_pattern = re.compile(r"Son script started with iteration (\d+)")
    son_delaying_pattern = re.compile(r"Son script delaying for iteration (\d+)")
    son_finished_pattern = re.compile(r"Son script finished with iteration (\d+)")
    father_wait_returned_pattern = re.compile(
        r"Father iteration (\d+): son finished, wait returned"
    )

    # Track which iterations completed
    father_calling = set()
    son_started = set()
    son_delaying = set()
    son_finished = set()
    wait_returned = set()

    def check_output(line: str) -> None:
        """Check log output for expected messages."""
        if test_complete.is_set():
            return

        if mo := father_calling_pattern.search(line):
            father_calling.add(int(mo.group(1)))
        elif mo := son_started_pattern.search(line):
            son_started.add(int(mo.group(1)))
        elif mo := son_delaying_pattern.search(line):
            son_delaying.add(int(mo.group(1)))
        elif mo := son_finished_pattern.search(line):
            son_finished.add(int(mo.group(1)))
        elif mo := father_wait_returned_pattern.search(line):
            iteration = int(mo.group(1))
            wait_returned.add(iteration)
            # Test completes when iteration 9 finishes
            if iteration == 9:
                test_complete.set()

    # Run with log monitoring
    async with (
        run_compiled(yaml_config, line_callback=check_output),
        api_client_connected() as client,
    ):
        # Verify device info
        device_info = await client.device_info()
        assert device_info is not None
        assert device_info.name == "test-script-delay-params"

        # Get services
        _, services = await client.list_entities_services()
        test_service = next(
            (s for s in services if s.name == "test_repeat_with_delay"), None
        )
        assert test_service is not None, "test_repeat_with_delay service not found"

        # Execute the test
        await client.execute_service(test_service, {})

        # Wait for test to complete (10 iterations * ~100ms each + margin)
        try:
            await asyncio.wait_for(test_complete.wait(), timeout=5.0)
        except TimeoutError:
            pytest.fail(
                f"Test timed out. Completed iterations: {sorted(wait_returned)}. "
                f"This likely indicates the script became a zombie (issue #12044)."
            )

        # Verify all 10 iterations completed successfully
        expected_iterations = set(range(10))
        assert father_calling == expected_iterations, "Not all iterations started"
        assert son_started == expected_iterations, (
            "Son script not started for all iterations"
        )
        assert son_finished == expected_iterations, (
            "Son script not finished for all iterations"
        )
        assert wait_returned == expected_iterations, (
            "script.wait did not return for all iterations"
        )

        # Verify delays were triggered for iterations >= 5
        expected_delays = set(range(5, 10))
        assert son_delaying == expected_delays, (
            "Delays not triggered for iterations >= 5"
        )
