"""Distributed merge lock using Redis SET NX EX."""

from __future__ import annotations

import uuid
from typing import Optional

from redis.asyncio import Redis

from loom.bus.channels import MERGE_LOCK_KEY

# Lua script for atomic check-and-delete (release only if token matches)
_RELEASE_SCRIPT = """
if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end
"""


async def acquire_merge_lock(
    redis_client: Redis, ttl_seconds: int = 300
) -> Optional[str]:
    """Attempt to acquire the merge lock.

    Uses SET key token NX EX atomically.

    Args:
        redis_client: Async Redis connection.
        ttl_seconds: Lock expiry in seconds (must be > 0).

    Returns:
        Lock token (str) on success, None if the lock is already held.

    Raises:
        ValueError: If ttl_seconds <= 0.
    """
    if ttl_seconds <= 0:
        raise ValueError(f"ttl_seconds must be positive, got {ttl_seconds}")

    token = uuid.uuid4().hex
    acquired = await redis_client.set(
        MERGE_LOCK_KEY, token, nx=True, ex=ttl_seconds
    )
    return token if acquired else None


async def release_merge_lock(redis_client: Redis, token: str) -> bool:
    """Release the merge lock if the token matches.

    Uses a Lua script for atomic check-and-delete.

    Args:
        redis_client: Async Redis connection.
        token: The token returned by acquire_merge_lock.

    Returns:
        True if the lock was released, False if token mismatch or key gone.

    Raises:
        ValueError: If token is empty.
    """
    if not token:
        raise ValueError("token must not be empty")

    result = await redis_client.eval(_RELEASE_SCRIPT, 1, MERGE_LOCK_KEY, token)
    return result == 1
