"""Tests for Sondeo async + concurrency support (v0.4.0)."""

from __future__ import annotations

import asyncio
import threading
import time

from sondeo.profiler.logging import clear_events
from sondeo.profiler.timer import (
    clear_results as clear_timer_results,
)
from sondeo.profiler.timer import (
    get_async_task_stats,
    get_concurrency_hints,
    profile,
    timer,
)
from sondeo.profiler.timer import (
    get_results as get_timer_results,
)
from sondeo.profiler.trace import clear_results as clear_trace_results
from sondeo.profiler.trace import trace


def _clear_all() -> None:
    clear_events()
    clear_timer_results()
    clear_trace_results()


def test_profile_supports_async_function() -> None:
    _clear_all()

    @profile
    async def async_job(value: int) -> int:
        await asyncio.sleep(0.01)
        return value * 2

    output = asyncio.run(async_job(21))
    assert output == 42

    results = get_timer_results()
    assert len(results) == 1
    assert results[0].is_async is True
    assert results[0].task_name is not None
    assert results[0].thread_name is not None

    _clear_all()


def test_timer_supports_async_with() -> None:
    _clear_all()

    async def run_async_timer() -> None:
        async with timer("async block") as t:
            await asyncio.sleep(0.008)
        assert t.is_async is True
        assert t.elapsed_ms > 0

    asyncio.run(run_async_timer())

    results = get_timer_results()
    assert len(results) == 1
    assert results[0].name == "async block"
    assert results[0].is_async is True

    _clear_all()


def test_profile_records_thread_metadata() -> None:
    _clear_all()

    @profile
    def worker() -> int:
        _ = sum(range(20_000))
        return 1

    threads = [
        threading.Thread(target=worker, name="worker-a"),
        threading.Thread(target=worker, name="worker-b"),
    ]
    for item in threads:
        item.start()
    for item in threads:
        item.join()

    results = [result for result in get_timer_results() if "worker" in result.name]
    assert len(results) == 2
    assert {result.thread_name for result in results} == {"worker-a", "worker-b"}

    _clear_all()


def test_async_task_tracking_detects_slow_coroutines() -> None:
    _clear_all()

    @profile
    async def do_work(delay: float) -> None:
        await asyncio.sleep(delay)

    async def run_tasks() -> None:
        slow = asyncio.create_task(do_work(0.03), name="slow-task")
        fast = asyncio.create_task(do_work(0.001), name="fast-task")
        await asyncio.gather(slow, fast)

    asyncio.run(run_tasks())

    stats = get_async_task_stats()
    assert len(stats) >= 2
    assert stats[0].task_name == "slow-task"
    assert stats[0].slowest_ms > stats[1].slowest_ms

    hints = get_concurrency_hints(slow_task_ms=5.0)
    assert any("slow coroutine task" in hint.lower() for hint in hints)

    _clear_all()


def test_trace_collects_per_thread_trees_and_contention_hints() -> None:
    _clear_all()

    def slow_thread() -> None:
        time.sleep(0.06)

    def fast_thread() -> None:
        time.sleep(0.002)

    with trace(filter_stdlib=False) as result:
        slow = threading.Thread(target=slow_thread, name="trace-slow")
        fast = threading.Thread(target=fast_thread, name="trace-fast")
        slow.start()
        fast.start()
        slow.join()
        fast.join()

    assert len(result.threads) >= 2
    assert any(item.thread_name == "trace-slow" for item in result.threads)
    assert any(item.thread_name == "trace-fast" for item in result.threads)
    assert result.contention_hints

    _clear_all()
