import threading

from promrs import CollectorRegistry, Counter, Gauge, Histogram, generate_latest


def test_counter_thread_safety() -> None:
    registry = CollectorRegistry()
    c = Counter("thread_counter", "Thread-safe counter", ["worker"], registry=registry)
    iterations = 1000

    def worker(worker_id: int) -> None:
        child = c.labels(worker=str(worker_id))
        for _ in range(iterations):
            child.inc()

    threads = [threading.Thread(target=worker, args=(i,)) for i in range(4)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    output = generate_latest(registry).decode()
    for i in range(4):
        assert f'thread_counter_total{{worker="{i}"}} {float(iterations)}' in output


def test_gauge_thread_safety() -> None:
    registry = CollectorRegistry()
    g = Gauge("thread_gauge", "Thread-safe gauge", ["worker"], registry=registry)

    def worker(worker_id: int) -> None:
        child = g.labels(worker=str(worker_id))
        for _ in range(100):
            child.inc()
            child.dec()
        child.set(42.0)

    threads = [threading.Thread(target=worker, args=(i,)) for i in range(4)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    output = generate_latest(registry).decode()
    for i in range(4):
        assert f'thread_gauge{{worker="{i}"}} 42.0' in output


def test_histogram_thread_safety() -> None:
    registry = CollectorRegistry()
    h = Histogram("thread_hist", "Thread-safe histogram", ["worker"], registry=registry, buckets=[0.5, 1.0])
    iterations = 100

    def worker(worker_id: int) -> None:
        child = h.labels(worker=str(worker_id))
        for _ in range(iterations):
            child.observe(0.1)

    threads = [threading.Thread(target=worker, args=(i,)) for i in range(4)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    output = generate_latest(registry).decode()
    for i in range(4):
        assert f'thread_hist_bucket{{le="0.5",worker="{i}"}} {iterations}' in output
        assert f'thread_hist_count{{worker="{i}"}} {iterations}' in output


def test_concurrent_generate_latest() -> None:
    registry = CollectorRegistry()
    c = Counter("concurrent_counter", "test", ["k"], registry=registry)
    c.labels(k="v").inc(10.0)
    results: list[bytes] = []
    lock = threading.Lock()

    def reader() -> None:
        for _ in range(50):
            data = generate_latest(registry)
            with lock:
                results.append(data)

    threads = [threading.Thread(target=reader) for _ in range(4)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    assert len(results) == 200
    for r in results:
        assert b'concurrent_counter_total{k="v"} 10.0' in r
