"""
H-MEM Background Consolidation Job.

Provides automatic background consolidation of episodes into traces
based on thresholds and time intervals.
"""

import asyncio
from datetime import datetime, timezone
from typing import Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from gsd_rlm.memory.hmem.store import EpisodeStore
    from gsd_rlm.memory.hmem.trace import TraceConsolidator


class ConsolidationJob:
    """
    Background job for automatic episode consolidation.

    Triggers consolidation based on:
    - Episode count threshold (unconsolidated episodes)
    - Time interval (periodic consolidation)
    - Manual trigger (run_once)
    """

    # Default threshold for triggering consolidation
    DEFAULT_EPISODE_THRESHOLD = 100
    DEFAULT_INTERVAL_SECONDS = 300  # 5 minutes

    def __init__(
        self,
        episode_store: "EpisodeStore",
        trace_consolidator: "TraceConsolidator",
        interval_seconds: int = DEFAULT_INTERVAL_SECONDS,
        episode_threshold: int = DEFAULT_EPISODE_THRESHOLD,
    ):
        """
        Initialize the consolidation job.

        Args:
            episode_store: Store to check for unconsolidated episodes.
            trace_consolidator: Consolidator to process episodes.
            interval_seconds: Interval between consolidation checks.
            episode_threshold: Minimum unconsolidated episodes to trigger.
        """
        self.episode_store = episode_store
        self.trace_consolidator = trace_consolidator
        self.interval_seconds = interval_seconds
        self.episode_threshold = episode_threshold

        # Job state
        self._running = False
        self._task: Optional[asyncio.Task] = None
        self._last_consolidation: Optional[str] = None

    async def start(self) -> None:
        """Start the background consolidation loop."""
        if self._running:
            return

        self._running = True
        self._task = asyncio.create_task(self._consolidation_loop())

    async def stop(self) -> None:
        """Stop the background consolidation loop."""
        self._running = False

        if self._task is not None:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass
            self._task = None

    async def run_once(self) -> int:
        """
        Run a single consolidation pass.

        Returns:
            Number of traces created.
        """
        return await self._do_consolidation()

    def _should_consolidate(self) -> bool:
        """
        Check if consolidation should be triggered.

        Returns:
            True if consolidation should run.
        """
        # Check unconsolidated episode count
        unconsolidated = self.episode_store.get_unconsolidated_episodes(
            limit=self.episode_threshold + 1
        )

        return len(unconsolidated) >= self.episode_threshold

    async def _consolidation_loop(self) -> None:
        """Main background consolidation loop."""
        while self._running:
            try:
                # Check if consolidation needed
                if self._should_consolidate():
                    await self._do_consolidation()

                # Wait for next interval
                await asyncio.sleep(self.interval_seconds)

            except asyncio.CancelledError:
                break
            except Exception:
                # Log error but continue running
                await asyncio.sleep(self.interval_seconds)

    async def _do_consolidation(self) -> int:
        """
        Perform consolidation of unconsolidated episodes.

        Returns:
            Number of traces created.
        """
        # Get unconsolidated episodes grouped by session
        unconsolidated = self.episode_store.get_unconsolidated_episodes(
            limit=self.episode_threshold * 2
        )

        if not unconsolidated:
            return 0

        # Group by session
        sessions: dict[str, list] = {}
        for episode in unconsolidated:
            if episode.session_id not in sessions:
                sessions[episode.session_id] = []
            sessions[episode.session_id].append(episode)

        # Consolidate each session
        traces_created = 0
        for session_id in sessions:
            try:
                trace = await self.trace_consolidator.consolidate_session(session_id)
                if trace is not None:
                    traces_created += 1
            except Exception:
                # Continue with other sessions on error
                continue

        # Update last consolidation time
        self._last_consolidation = datetime.now(timezone.utc).isoformat()

        return traces_created

    @property
    def is_running(self) -> bool:
        """Check if the job is currently running."""
        return self._running

    @property
    def last_consolidation(self) -> Optional[str]:
        """Get ISO timestamp of last consolidation."""
        return self._last_consolidation
