"""Progress tracking service using Redis.

This module provides progress tracking for document processing using Redis
as an ephemeral data store. Progress data expires after 1 hour.
"""

from enum import Enum
from typing import Optional
from uuid import UUID

import redis.asyncio as redis
import structlog

from src.core.config import settings

logger = structlog.get_logger(__name__)


class ProcessingStage(str, Enum):
    """Document processing stages."""

    VALIDATING = "validating"
    EXTRACTING_TABLES = "extracting_tables"
    RESOLVING_SYMBOLS = "resolving_symbols"
    INDEXING = "indexing"


class ProgressTracker:
    """Redis-based progress tracker for document processing."""

    def __init__(self, redis_url: str):
        """Initialize progress tracker with Redis connection.

        Args:
            redis_url: Redis connection URL.
        """
        self._redis = redis.from_url(redis_url, decode_responses=True)

    async def set_progress(
        self,
        document_id: UUID,
        stage: ProcessingStage | str,
        sheets_processed: int,
        sheets_total: int,
    ) -> None:
        """Set document processing progress in Redis.

        Args:
            document_id: Document UUID.
            stage: Current processing stage.
            sheets_processed: Number of sheets processed so far.
            sheets_total: Total number of sheets to process.
        """
        key = f"progress:{document_id}"

        # Convert enum to string if needed
        stage_str = stage.value if isinstance(stage, ProcessingStage) else stage

        data = {
            "stage": stage_str,
            "sheets_processed": str(sheets_processed),
            "sheets_total": str(sheets_total),
        }

        try:
            await self._redis.hset(key, mapping=data)
            # Set TTL of 1 hour
            await self._redis.expire(key, 3600)

            logger.info(
                "progress_updated",
                document_id=str(document_id),
                stage=stage_str,
                sheets_processed=sheets_processed,
                sheets_total=sheets_total,
            )
        except redis.RedisError as e:
            logger.error(
                "progress_update_failed",
                document_id=str(document_id),
                error=str(e),
            )
            # Don't raise - progress tracking is non-critical
            # Processing should continue even if Redis is unavailable

    async def get_progress(
        self, document_id: UUID
    ) -> Optional[dict[str, str | int]]:
        """Get document processing progress from Redis.

        Args:
            document_id: Document UUID.

        Returns:
            Dictionary with progress data (stage, sheets_processed, sheets_total)
            or None if not found.
        """
        key = f"progress:{document_id}"

        try:
            data = await self._redis.hgetall(key)

            if not data:
                logger.debug(
                    "progress_not_found",
                    document_id=str(document_id),
                )
                return None

            return {
                "stage": data["stage"],
                "sheets_processed": int(data["sheets_processed"]),
                "sheets_total": int(data["sheets_total"]),
            }
        except redis.RedisError as e:
            logger.error(
                "progress_fetch_failed",
                document_id=str(document_id),
                error=str(e),
            )
            # Return None - graceful degradation if Redis is unavailable
            return None
        except (KeyError, ValueError) as e:
            logger.error(
                "progress_parse_failed",
                document_id=str(document_id),
                error=str(e),
            )
            return None

    async def clear_progress(self, document_id: UUID) -> None:
        """Clear document processing progress from Redis.

        Args:
            document_id: Document UUID.
        """
        key = f"progress:{document_id}"

        try:
            await self._redis.delete(key)
            logger.debug(
                "progress_cleared",
                document_id=str(document_id),
            )
        except redis.RedisError as e:
            logger.error(
                "progress_clear_failed",
                document_id=str(document_id),
                error=str(e),
            )
            # Don't raise - cleanup is non-critical

    async def close(self) -> None:
        """Close Redis connection."""
        await self._redis.aclose()


# Global progress tracker instance
_progress_tracker: Optional[ProgressTracker] = None


def get_progress_tracker() -> ProgressTracker:
    """Get or create the global progress tracker instance.

    Returns:
        ProgressTracker instance.
    """
    global _progress_tracker
    if _progress_tracker is None:
        _progress_tracker = ProgressTracker(settings.redis_url)
    return _progress_tracker
