"""
Progress Tracker Service (Story 2.11)

Provides real-time progress tracking for translation jobs.
Designed for O(1) updates and < 500ms latency (NFR3).
"""

from typing import Dict, Any, Optional, Callable
import threading
import time


class ProgressTracker:
    """
    Track translation progress with callback support.

    Designed for high-performance updates with minimal overhead.
    Uses in-memory storage for MVP (consistent with Story 2.10 pattern).

    Usage:
        storage = {}  # Reference to _translation_jobs dict
        tracker = ProgressTracker("job_123", storage)
        tracker.update(50, "Translating sheet 2/4")

        # Or use item-based progress
        tracker.update_item(3, 10, "Translating slide")
    """

    def __init__(self, job_id: str, storage: Dict[str, Any]):
        """
        Initialize progress tracker.

        Args:
            job_id: The translation job ID
            storage: Reference to the job storage dict (e.g., _translation_jobs)
        """
        self.job_id = job_id
        self.storage = storage
        self._lock = threading.RLock()
        self._last_update_time = 0
        self._min_update_interval = 0.05  # 50ms minimum between updates (throttling)

    def update(self, percent: int, step: str) -> None:
        """
        Update progress percentage and current step.

        Thread-safe and throttled to prevent excessive updates.

        Args:
            percent: Progress percentage (0-100), will be clamped
            step: Human-readable description of current operation
        """
        with self._lock:
            current_time = time.time()
            if current_time - self._last_update_time < self._min_update_interval:
                if percent < 100:
                    return

            job = self.storage.get(self.job_id)
            if job:
                # Never decrease progress — only move forward.
                new_percent = min(100, max(0, percent))
                job["progress_percent"] = max(job.get("progress_percent", 0), new_percent)
                job["current_step"] = step
                job["processed_items"] = job.get("processed_items", 0)
                job["total_items"] = job.get("total_items", 0)
                self._last_update_time = current_time

    def update_item(
        self, current: int, total: int, item_name: str, max_percent: int = 100
    ) -> None:
        """
        Update progress based on item count (e.g., slides, sheets).

        Calculates percentage from current/total and formats step message.

        Args:
            current: Current item number (1-based)
            total: Total number of items
            item_name: Name of item type (e.g., "Translating slide", "Processing sheet")
            max_percent: Upper bound for the computed percentage (default 100).
                         Use 95 to reserve the last 5% for file-save + set_completed().
        """
        percent = int((current / total) * 100) if total > 0 else 0
        percent = min(percent, max_percent)
        step = f"{item_name} {current}/{total}"

        with self._lock:
            current_time = time.time()
            if current_time - self._last_update_time < self._min_update_interval:
                if percent < 100:
                    return

            job = self.storage.get(self.job_id)
            if job:
                # Never decrease progress — only move forward.
                new_percent = min(100, max(0, percent))
                job["progress_percent"] = max(job.get("progress_percent", 0), new_percent)
                job["current_step"] = step
                job["processed_items"] = current
                job["total_items"] = total
                self._last_update_time = current_time

    def set_error(
        self, error_message: str, step: str = "Error during translation"
    ) -> None:
        """
        Mark job as failed with error message.

        Args:
            error_message: Description of the error
            step: Current step description (default: "Error during translation")
        """
        with self._lock:
            job = self.storage.get(self.job_id)
            if job:
                job["status"] = "failed"
                job["error_message"] = error_message
                job["current_step"] = step
                job["failed_at"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

    def set_completed(self, output_path: Optional[str] = None) -> None:
        """
        Mark job as completed.

        Args:
            output_path: Optional path to the output file
        """
        with self._lock:
            job = self.storage.get(self.job_id)
            if job:
                job["status"] = "completed"
                job["progress_percent"] = 100
                job["current_step"] = "Translation complete"
                job["completed_at"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
                if output_path:
                    job["output_path"] = str(output_path)


def create_progress_callback(
    tracker: ProgressTracker, item_name: str, total_items: int
) -> Callable[[Dict[str, Any]], None]:
    """
    Create a progress callback function for use with translators.

    Args:
        tracker: ProgressTracker instance
        item_name: Name of item being processed (e.g., "Translating slide")
        total_items: Total number of items

    Returns:
        Callback function compatible with translator progress_callback parameter
    """

    def callback(progress_info: Dict[str, Any]) -> None:
        """Progress callback that updates the tracker."""
        # Extract item number from progress_info dict
        # Different translators use different keys
        current = progress_info.get(
            "slide",
            progress_info.get(
                "sheet", progress_info.get("paragraph", progress_info.get("element", 1))
            ),
        )
        total = progress_info.get(
            "total_slides",
            progress_info.get(
                "total", progress_info.get("total_paragraphs", total_items)
            ),
        )

        tracker.update_item(current, total, item_name)

    return callback
