import asyncio
import time
import asyncpg
from app.models.models import Target
from app.schemas.overview import DatabaseOverviewOut
from app.services.collector import build_target_dsn
from app.services.infra_probe import get_disk_space_provider
from app.services.overview_collector import collect_overview

_CACHE_TTL_SECONDS = 20
_cache: dict[int, tuple[float, DatabaseOverviewOut]] = {}
_locks: dict[int, asyncio.Lock] = {}


def _get_lock(target_id: int) -> asyncio.Lock:
    if target_id not in _locks:
        _locks[target_id] = asyncio.Lock()
    return _locks[target_id]


async def get_target_overview(target: Target) -> DatabaseOverviewOut:
    now = time.time()
    cached = _cache.get(target.id)
    if cached and (now - cached[0]) < _CACHE_TTL_SECONDS:
        return cached[1]

    lock = _get_lock(target.id)
    async with lock:
        cached = _cache.get(target.id)
        now = time.time()
        if cached and (now - cached[0]) < _CACHE_TTL_SECONDS:
            return cached[1]

        conn = await asyncpg.connect(dsn=build_target_dsn(target))
        try:
            payload = await collect_overview(
                conn=conn,
                disk_provider=get_disk_space_provider(),
                target_host=target.host,
                cache_ttl_seconds=_CACHE_TTL_SECONDS,
            )
        finally:
            await conn.close()

        _cache[target.id] = (time.time(), payload)
        return payload
