# co-gahusb/app/locks.py
from redis.exceptions import WatchError

LOCK_PREFIX = "co:lock:"


async def acquire_lock(r, resource, role, ttl_sec=300):
    key = LOCK_PREFIX + resource
    ok = await r.set(key, role, nx=True, ex=ttl_sec)
    if ok:
        return {"acquired": True}
    held_by = await r.get(key)
    ttl = await r.ttl(key)
    return {"acquired": False, "held_by": held_by, "ttl_remaining": max(ttl, 0)}


async def release_lock(r, resource, role):
    key = LOCK_PREFIX + resource
    async with r.pipeline() as pipe:
        while True:
            try:
                await pipe.watch(key)
                owner = await pipe.get(key)
                if owner != role:
                    await pipe.unwatch()
                    return {"released": False, "held_by": owner}
                pipe.multi()
                pipe.delete(key)
                await pipe.execute()
                return {"released": True}
            except WatchError:
                continue


async def heartbeat_lock(r, resource, role, ttl_sec=300):
    key = LOCK_PREFIX + resource
    async with r.pipeline() as pipe:
        while True:
            try:
                await pipe.watch(key)
                owner = await pipe.get(key)
                if owner != role:
                    await pipe.unwatch()
                    return {"renewed": False, "held_by": owner}
                pipe.multi()
                pipe.expire(key, ttl_sec)
                await pipe.execute()
                return {"renewed": True}
            except WatchError:
                continue


async def list_locks(r):
    keys = await r.keys(LOCK_PREFIX + "*")
    out = []
    for key in keys:
        held_by = await r.get(key)
        if held_by is None:
            continue
        ttl = await r.ttl(key)
        out.append({
            "resource": key[len(LOCK_PREFIX):],
            "held_by": held_by,
            "ttl_remaining": max(ttl, 0),
        })
    return {"locks": out}
