import asyncio from dataclasses import dataclass from datetime import UTC, datetime from uuid import UUID, uuid4 from app.models import Monitor, MonitorCreate, MonitorUpdate, StatusSnapshot @dataclass class _Record: monitor: Monitor revision: int class MonitorStore: def __init__(self) -> None: self._lock = asyncio.Lock() self._records: dict[UUID, _Record] = {} async def create(self, data: MonitorCreate) -> Monitor: now = datetime.now(UTC) monitor = Monitor( id=uuid4(), name=data.name, url=data.url, created_at=now, updated_at=now, status=StatusSnapshot(), ) async with self._lock: self._records[monitor.id] = _Record(monitor, 0) return monitor.model_copy(deep=True) async def list(self) -> list[Monitor]: async with self._lock: values = [item.monitor for item in self._records.values()] return [value.model_copy(deep=True) for value in values] async def get(self, monitor_id: UUID) -> Monitor | None: async with self._lock: record = self._records.get(monitor_id) return record.monitor.model_copy(deep=True) if record else None async def update(self, monitor_id: UUID, data: MonitorUpdate) -> Monitor | None: async with self._lock: record = self._records.get(monitor_id) if not record: return None url_changed = record.monitor.url != data.url status = StatusSnapshot() if url_changed else record.monitor.status record.monitor = record.monitor.model_copy(update={ "name": data.name, "url": data.url, "updated_at": datetime.now(UTC), "status": status, }) record.revision += 1 return record.monitor.model_copy(deep=True) async def delete(self, monitor_id: UUID) -> bool: async with self._lock: return self._records.pop(monitor_id, None) is not None async def begin_check(self, monitor_id: UUID) -> tuple[Monitor, int] | None: async with self._lock: record = self._records.get(monitor_id) if not record: return None return record.monitor.model_copy(deep=True), record.revision async def finish_check( self, monitor_id: UUID, revision: int, status: StatusSnapshot, ) -> bool: async with self._lock: record = self._records.get(monitor_id) if not record or record.revision != revision: return False record.monitor = record.monitor.model_copy(update={ "status": status, "updated_at": datetime.now(UTC), }) return True