"""Tests for mutation safety: per-partition locking and dead code removal (MUT-01, MUT-02)."""

from __future__ import annotations

import threading
import time
from pathlib import Path


def test_per_partition_lock_concurrency():
    """Two threads acquiring locks for different partitions can hold locks simultaneously."""
    from opendeviationbar.clickhouse.maintenance import _get_partition_lock

    lock_a = _get_partition_lock("BTCUSDT", 250)
    lock_b = _get_partition_lock("ETHUSDT", 500)

    # Both threads acquire their locks; we verify they don't block each other.
    results: dict[str, float] = {}
    barrier = threading.Barrier(2)

    def hold_lock(name: str, lock: threading.Lock) -> None:
        lock.acquire()
        try:
            barrier.wait(timeout=2)  # both must reach here while holding locks
            results[name] = time.monotonic()
            time.sleep(0.05)
        finally:
            lock.release()

    t1 = threading.Thread(target=hold_lock, args=("a", lock_a))
    t2 = threading.Thread(target=hold_lock, args=("b", lock_b))

    start = time.monotonic()
    t1.start()
    t2.start()
    t1.join(timeout=3)
    t2.join(timeout=3)

    # Both threads held locks concurrently (barrier passed within 2s)
    assert "a" in results and "b" in results, "Both threads should complete"
    assert abs(results["a"] - results["b"]) < 0.1, (
        "Different-partition locks should be held simultaneously"
    )


def test_same_partition_serialized():
    """Two threads acquiring lock for same partition are serialized."""
    from opendeviationbar.clickhouse.maintenance import _get_partition_lock

    lock = _get_partition_lock("BTCUSDT", 250)
    order: list[str] = []
    event = threading.Event()

    def first():
        with lock:
            order.append("first_acquired")
            event.set()  # signal that first holds lock
            time.sleep(0.1)
            order.append("first_released")

    def second():
        event.wait(timeout=2)  # wait until first holds lock
        time.sleep(0.01)  # small delay to ensure first is still holding
        with lock:
            order.append("second_acquired")

    t1 = threading.Thread(target=first)
    t2 = threading.Thread(target=second)
    t1.start()
    t2.start()
    t1.join(timeout=3)
    t2.join(timeout=3)

    # Second must acquire AFTER first releases
    assert order == ["first_acquired", "first_released", "second_acquired"], (
        f"Same-partition locks must serialize, got: {order}"
    )


def test_get_partition_lock_returns_same_instance():
    """Calling _get_partition_lock twice with same args returns the same Lock object."""
    from opendeviationbar.clickhouse.maintenance import _get_partition_lock

    lock1 = _get_partition_lock("BTCUSDT", 250)
    lock2 = _get_partition_lock("BTCUSDT", 250)
    assert lock1 is lock2, "Same (symbol, threshold) should return identical Lock instance"


def test_dead_code_removed():
    """The `if False:` block (old fallback DELETE) no longer exists in maintenance.py."""
    source = Path(__file__).resolve().parent.parent / "python" / "opendeviationbar" / "clickhouse" / "maintenance.py"
    content = source.read_text()
    assert "if False:" not in content, (
        "Dead code `if False:` block should be removed from maintenance.py"
    )
