"""Tests for parallel (threaded) execution of reduce_file."""
import threading
import time
from delta_reduce.core import reduce_file


def test_parallel_reaches_same_result_as_sequential():
    """n_threads=4 should produce the same final file as n_threads=1."""
    lines = [f"line{i}\n" for i in range(20)]
    essential = "line19\n"

    def test_fn(candidate: list[str], _) -> bool:
        return essential in candidate

    result_seq, _ = reduce_file(list(lines), test_fn, n_threads=1)
    result_par, _ = reduce_file(list(lines), test_fn, n_threads=4)

    assert result_seq == [essential]
    assert result_par == [essential]


def test_parallel_invokes_test_fn_concurrently():
    """Multiple threads must be running test_fn at the same time."""
    lines = [f"line{i}\n" for i in range(20)]
    essential = "line19\n"

    concurrent_count = 0
    peak_concurrent = 0
    lock = threading.Lock()

    def test_fn(candidate: list[str], _) -> bool:
        nonlocal concurrent_count, peak_concurrent
        with lock:
            concurrent_count += 1
            if concurrent_count > peak_concurrent:
                peak_concurrent = concurrent_count
        time.sleep(0.01)  # hold long enough to overlap
        with lock:
            concurrent_count -= 1
        return essential in candidate

    reduce_file(list(lines), test_fn, n_threads=4)
    assert peak_concurrent > 1, f"Expected concurrent execution, peak was {peak_concurrent}"


def test_test_fn_called_with_is_cancelled():
    """test_fn must receive a callable as its second argument that returns bool."""
    lines = ["a\n", "b\n"]
    received_second_args = []

    def test_fn(candidate, is_cancelled):
        received_second_args.append(is_cancelled)
        return "a\n" in candidate

    reduce_file(list(lines), test_fn)
    assert received_second_args, "test_fn was never called"
    assert all(callable(f) for f in received_second_args)
    assert all(isinstance(f(), bool) for f in received_second_args)


def test_early_cancellation_on_commit():
    """When a commit is applied, in-flight is_cancelled() must return True."""
    lines = [f"line{i}\n" for i in range(10)]
    essential = "line0\n"  # first line: Thread 2's first chunk contains it, commits immediately
    cancel_seen = threading.Event()

    def test_fn(candidate, is_cancelled):
        if essential not in candidate:
            # Poll is_cancelled every 50 ms for up to 0.4 s (simulates a slow test).
            for _ in range(8):
                time.sleep(0.05)
                if is_cancelled():
                    cancel_seen.set()
                    return False
            return False
        return True

    t0 = time.monotonic()
    reduce_file(list(lines), test_fn, n_threads=2)
    elapsed = time.monotonic() - t0

    assert cancel_seen.is_set(), "No in-flight test_fn saw is_cancelled() return True after a commit"
    # Without cancellation 2 threads * ~10 non-essential iterations * 0.4 s ≈ 4 s.
    # With cancellation most stale tests bail quickly → well under 2.0 s.
    assert elapsed < 2.0, f"Expected early cancellation, but took {elapsed:.1f}s"
