"""
ABOS AI Worker – Transient In-Memory Document Store
=====================================================
Provides a thread-safe, TTL-aware in-memory store for document records.
Documents are automatically evicted after `document_ttl_seconds` (configured
in settings) to prevent unbounded memory growth.

Design notes
------------
* Pure in-memory — no database connection required.
* Thread-safe via asyncio.Lock (FastAPI runs in a single async event-loop).
* For multi-instance / distributed deployments, swap this out for a Redis
  adapter that honours the same interface (get / set / delete).
"""

from __future__ import annotations

import asyncio
import time
import uuid
from typing import Optional

from app.config import settings
from app.schemas import DocumentRecord


class _DocumentStore:
    """Internal singleton store keyed by document_id (UUID string)."""

    def __init__(self) -> None:
        self._store: dict[str, tuple[DocumentRecord, float]] = {}
        # Timestamps: {document_id: created_at (epoch seconds)}
        self._lock = asyncio.Lock()

    # ------------------------------------------------------------------
    # Public helpers
    # ------------------------------------------------------------------

    async def save(self, record: DocumentRecord) -> None:
        """Insert or fully replace a document record."""
        async with self._lock:
            self._store[record.document_id] = (record, time.monotonic())

    async def get(self, document_id: str) -> Optional[DocumentRecord]:
        """Return the record, or None if not found / expired."""
        async with self._lock:
            entry = self._store.get(document_id)
            if entry is None:
                return None
            record, created_at = entry
            if self._is_expired(created_at):
                del self._store[document_id]
                return None
            return record

    async def update(self, record: DocumentRecord) -> None:
        """Update an existing record; preserves the original creation timestamp."""
        async with self._lock:
            entry = self._store.get(record.document_id)
            if entry is None:
                # If somehow missing, treat as a new save
                self._store[record.document_id] = (record, time.monotonic())
            else:
                _, created_at = entry
                self._store[record.document_id] = (record, created_at)

    async def delete(self, document_id: str) -> bool:
        """Remove a record. Returns True if the record existed."""
        async with self._lock:
            return self._store.pop(document_id, None) is not None

    async def purge_expired(self) -> int:
        """Remove all expired records. Returns the number of records purged."""
        async with self._lock:
            now = time.monotonic()
            expired = [
                doc_id
                for doc_id, (_, created_at) in self._store.items()
                if self._is_expired(created_at, now)
            ]
            for doc_id in expired:
                del self._store[doc_id]
            return len(expired)

    # ------------------------------------------------------------------
    # Static helpers
    # ------------------------------------------------------------------

    @staticmethod
    def generate_id() -> str:
        """Generate a new unique document UUID."""
        return str(uuid.uuid4())

    @staticmethod
    def _is_expired(created_at: float, now: float | None = None) -> bool:
        if now is None:
            now = time.monotonic()
        return (now - created_at) > settings.document_ttl_seconds


# ---------------------------------------------------------------------------
# Module-level singleton – import and use this everywhere
# ---------------------------------------------------------------------------
document_store = _DocumentStore()
