"""
Realtime Progress Tracker Engine
Tracks production progress in real-time with detailed reporting.
"""

import time
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, field
from enum import Enum


class ProductionStatus(Enum):
    """Production status states."""
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"


@dataclass
class ProductionStep:
    """Represents a single step in the production process."""
    step_name: str
    status: str
    details: Dict[str, Any] = field(default_factory=dict)
    timestamp: float = field(default_factory=time.time)
    error_message: Optional[str] = None


@dataclass
class Production:
    """Represents an active or completed production."""
    production_id: str
    description: str
    total_steps: int
    completed_steps: int = 0
    current_step: Optional[str] = None
    status: ProductionStatus = ProductionStatus.IN_PROGRESS
    errors: List[str] = field(default_factory=list)
    start_time: float = field(default_factory=time.time)
    end_time: Optional[float] = None
    steps: List[ProductionStep] = field(default_factory=list)
    result: Dict[str, Any] = field(default_factory=dict)


class RealtimeProgressTracker:
    """
    Tracks production progress in real-time with detailed reporting.
    
    Stores production data in-memory using a dictionary.
    Supports multiple concurrent productions.
    """
    
    def __init__(self):
        """
        Initialize the progress tracker.
        """
        # In-memory storage for productions
        self._productions: Dict[str, Production] = {}
    
    def start_production(
        self,
        production_id: str,
        total_steps: int,
        description: str
    ) -> None:
        """
        Start tracking a new production.
        
        Args:
            production_id: Unique identifier for the production
            total_steps: Total number of expected steps
            description: Human-readable description of the production
        """
        if production_id in self._productions:
            raise ValueError(f"Production '{production_id}' already exists")
        
        production = Production(
            production_id=production_id,
            description=description,
            total_steps=total_steps,
            start_time=time.time()
        )
        
        self._productions[production_id] = production
    
    def report_step(
        self,
        production_id: str,
        step_name: str,
        status: str,
        details: Dict[str, Any]
    ) -> None:
        """
        Report a step completion or update.
        
        Args:
            production_id: Production identifier
            step_name: Name of the step being reported
            status: Status of the step (e.g., "running", "completed", "failed")
            details: Additional details about the step
        """
        if production_id not in self._productions:
            raise ValueError(f"Production '{production_id}' not found")
        
        production = self._productions[production_id]
        
        # Create step record
        step = ProductionStep(
            step_name=step_name,
            status=status,
            details=details
        )
        production.steps.append(step)
        
        # Update current step
        production.current_step = step_name
        
        # Increment completed steps if status indicates completion
        if status in ("completed", "success", "done"):
            production.completed_steps += 1
        
        # Update production status based on progress
        if production.completed_steps >= production.total_steps:
            production.status = ProductionStatus.COMPLETED
    
    def report_error(
        self,
        production_id: str,
        step_name: str,
        error_message: str
    ) -> None:
        """
        Report an error in a step.
        
        Args:
            production_id: Production identifier
            step_name: Name of the step where error occurred
            error_message: Error message or description
        """
        if production_id not in self._productions:
            raise ValueError(f"Production '{production_id}' not found")
        
        production = self._productions[production_id]
        
        # Add error to list
        error_entry = f"[{step_name}] {error_message}"
        production.errors.append(error_entry)
        
        # Create error step record
        step = ProductionStep(
            step_name=step_name,
            status="failed",
            error_message=error_message
        )
        production.steps.append(step)
        
        # Mark production as failed
        production.status = ProductionStatus.FAILED
        production.current_step = step_name
    
    def get_status(self, production_id: str) -> Dict[str, Any]:
        """
        Get current status of a production.
        
        Returns dict with:
        - production_id: str
        - description: str
        - total_steps: int
        - completed_steps: int
        - current_step: Optional[str]
        - status: str ("in_progress", "completed", "failed")
        - errors: List[str]
        - start_time: float
        - elapsed_time: float (seconds)
        - estimated_remaining: Optional[float] (seconds or None)
        
        Args:
            production_id: Production identifier
            
        Returns:
            Dict containing current production status
        """
        if production_id not in self._productions:
            raise ValueError(f"Production '{production_id}' not found")
        
        production = self._productions[production_id]
        current_time = time.time()
        elapsed_time = current_time - production.start_time
        
        # Calculate estimated remaining time
        estimated_remaining = None
        if production.status == ProductionStatus.IN_PROGRESS and production.completed_steps > 0:
            avg_time_per_step = elapsed_time / production.completed_steps
            remaining_steps = production.total_steps - production.completed_steps
            estimated_remaining = avg_time_per_step * remaining_steps
        
        return {
            "production_id": production.production_id,
            "description": production.description,
            "total_steps": production.total_steps,
            "completed_steps": production.completed_steps,
            "current_step": production.current_step,
            "status": production.status.value,
            "errors": production.errors.copy(),
            "start_time": production.start_time,
            "elapsed_time": elapsed_time,
            "estimated_remaining": estimated_remaining
        }
    
    def complete_production(
        self,
        production_id: str,
        result: Dict[str, Any]
    ) -> None:
        """
        Mark a production as completed.
        
        Args:
            production_id: Production identifier
            result: Result data to store with the production
        """
        if production_id not in self._productions:
            raise ValueError(f"Production '{production_id}' not found")
        
        production = self._productions[production_id]
        
        production.status = ProductionStatus.COMPLETED
        production.end_time = time.time()
        production.result = result
        production.current_step = None
    
    def get_all_productions(self) -> List[str]:
        """
        Get a list of all production IDs.
        
        Returns:
            List of production IDs
        """
        return list(self._productions.keys())
    
    def get_production_history(self, production_id: str) -> List[Dict[str, Any]]:
        """
        Get the full step history for a production.
        
        Args:
            production_id: Production identifier
            
        Returns:
            List of step dictionaries
        """
        if production_id not in self._productions:
            raise ValueError(f"Production '{production_id}' not found")
        
        production = self._productions[production_id]
        
        return [
            {
                "step_name": step.step_name,
                "status": step.status,
                "details": step.details,
                "timestamp": step.timestamp,
                "error_message": step.error_message
            }
            for step in production.steps
        ]
    
    def cancel_production(self, production_id: str) -> None:
        """
        Cancel and remove a production from tracking.
        
        Args:
            production_id: Production identifier
        """
        if production_id not in self._productions:
            raise ValueError(f"Production '{production_id}' not found")
        
        del self._productions[production_id]
    
    def clear_completed(self, max_age_seconds: Optional[float] = None) -> int:
        """
        Clear completed or failed productions.
        
        Args:
            max_age_seconds: If provided, only clear productions older than this
            
        Returns:
            Number of productions cleared
        """
        current_time = time.time()
        to_clear = []
        
        for production_id, production in self._productions.items():
            if production.status in (ProductionStatus.COMPLETED, ProductionStatus.FAILED):
                if max_age_seconds is None:
                    to_clear.append(production_id)
                elif production.end_time and (current_time - production.end_time) > max_age_seconds:
                    to_clear.append(production_id)
        
        for production_id in to_clear:
            del self._productions[production_id]
        
        return len(to_clear)
