"""
Pending event store for crash recovery.

Persists critical events that haven't been acknowledged to disk,
so they can be replayed after a container restart.
"""

import json
import logging
from pathlib import Path
from typing import List, Optional
import asyncio
import aiofiles

from .types import UserEvent

logger = logging.getLogger(__name__)


class PendingStore:
    """
    Persists pending critical events to disk for crash recovery.

    Events are written to a JSON file when the publisher shuts down,
    and loaded back when it starts up.
    """

    def __init__(self, path: Optional[str] = None):
        """
        Initialize pending store.

        Args:
            path: Path to the pending events file.
                  If None, persistence is disabled.
        """
        self.path = Path(path) if path else None
        self._lock = asyncio.Lock()

    async def save_pending(self, events: List[UserEvent]) -> None:
        """
        Save pending events to disk.

        Args:
            events: List of events to persist
        """
        if not self.path:
            return

        if not events:
            # No events to save, remove file if exists
            await self._remove_file()
            return

        async with self._lock:
            try:
                # Ensure parent directory exists
                self.path.parent.mkdir(parents=True, exist_ok=True)

                # Serialize events
                data = [event.to_dict() for event in events]
                json_str = json.dumps(data, indent=2)

                # Write atomically (write to temp, then rename)
                temp_path = self.path.with_suffix(".tmp")
                async with aiofiles.open(temp_path, "w") as f:
                    await f.write(json_str)

                # Atomic rename
                temp_path.rename(self.path)

                logger.info("Saved %d pending events to %s", len(events), self.path)

            except Exception as e:
                logger.error("Failed to save pending events: %s", e, exc_info=True)

    async def load_pending(self) -> List[UserEvent]:
        """
        Load pending events from disk.

        Returns:
            List of pending events, or empty list if none/error
        """
        if not self.path or not self.path.exists():
            return []

        async with self._lock:
            try:
                async with aiofiles.open(self.path, "r") as f:
                    content = await f.read()

                data = json.loads(content)
                events = [UserEvent.from_dict(d) for d in data]

                # Remove file after successful load
                await self._remove_file()

                logger.info("Loaded %d pending events from %s", len(events), self.path)
                return events

            except json.JSONDecodeError as e:
                logger.error("Failed to parse pending events file: %s", e)
                await self._remove_file()
                return []

            except Exception as e:
                logger.error("Failed to load pending events: %s", e, exc_info=True)
                return []

    async def _remove_file(self) -> None:
        """Remove the pending events file."""
        if self.path and self.path.exists():
            try:
                self.path.unlink()
                logger.debug("Removed pending events file: %s", self.path)
            except Exception as e:
                logger.warning("Failed to remove pending events file: %s", e)

    def has_pending(self) -> bool:
        """Check if there are pending events on disk."""
        return self.path is not None and self.path.exists()
