"""Distributed healing locks to prevent concurrent fixes for the same test.

Uses database-backed locking with TTL to ensure only one self-healing
operation runs per test at a time. This prevents duplicate PRs.
"""

from __future__ import annotations

import time
from dataclasses import dataclass


@dataclass
class LockInfo:
    """Information about an acquired lock."""

    org_id: str
    test_name: str
    acquired_at: float
    ttl_seconds: float

    @property
    def is_expired(self) -> bool:
        return time.time() - self.acquired_at > self.ttl_seconds


class HealingLock:
    """Database-backed distributed lock for self-healing operations.

    Prevents two concurrent healing runs from targeting the same test,
    which would create duplicate PRs.

    Uses a simple lock table with TTL-based expiration. Lock acquisition
    is atomic via INSERT ON CONFLICT DO NOTHING.
    """

    def __init__(self, ttl_seconds: float = 600, db_acquire=None, db_release=None):
        """Initialize healing lock.

        Args:
            ttl_seconds: Lock TTL in seconds (default 10 minutes)
            db_acquire: Optional custom acquire function for testing
            db_release: Optional custom release function for testing
        """
        self.ttl_seconds = ttl_seconds
        self._db_acquire = db_acquire
        self._db_release = db_release

    async def acquire(self, org_id: str, test_name: str) -> bool:
        """Try to acquire a lock for a test.

        Returns True if lock was acquired, False if already locked.
        Expired locks are cleaned up automatically.
        """
        if self._db_acquire:
            return await self._db_acquire(org_id, test_name, self.ttl_seconds)

        import asyncio
        from api.db import acquire_healing_lock
        return await asyncio.to_thread(acquire_healing_lock, org_id, test_name, self.ttl_seconds)

    async def release(self, org_id: str, test_name: str) -> None:
        """Release a lock for a test."""
        if self._db_release:
            await self._db_release(org_id, test_name)
            return

        import asyncio
        from api.db import release_healing_lock
        await asyncio.to_thread(release_healing_lock, org_id, test_name)
