"""
Job status poller.

Polls job status until completion or timeout.
Follows CLAUDE.md: Single responsibility, <10 methods, <50 lines/method.
"""

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

from .client import APIClient


class JobFailedException(Exception):
    """
    Exception raised when a job fails.

    Carries structured error details from the worker.
    """
    def __init__(self, message: str, job_id: str, error_details: Dict[str, Any] = None):
        super().__init__(message)
        self.job_id = job_id
        self.error_details = error_details


class JobPoller:
    """
    Poll job status until completion.

    Single responsibility: Status polling logic.
    Dependencies injected: API client.
    """

    def __init__(self, api_client: APIClient, poll_interval: int = 5):
        """
        Initialize job poller.

        Args:
            api_client: API client for status requests
            poll_interval: Seconds between polls (default: 5)
        """
        self.api_client = api_client
        self.poll_interval = poll_interval

    def wait_for_completion(
        self,
        job_id: str,
        timeout: int = 3600,
        callback: Optional[Callable[[Dict[str, Any]], None]] = None,
    ) -> Dict[str, Any]:
        """
        Poll job until completed or failed.

        Args:
            job_id: Job ID to poll
            timeout: Maximum seconds to wait (default: 3600 = 1 hour)
            callback: Optional callback for status updates

        Returns:
            Final job status dictionary

        Raises:
            TimeoutError: If timeout exceeded
            RuntimeError: If job failed
        """
        start_time = time.time()

        while True:
            # Check timeout
            if time.time() - start_time > timeout:
                raise TimeoutError(
                    f"Job {job_id} did not complete within {timeout} seconds"
                )

            # Poll status
            status_data = self._poll_once(job_id)

            # Notify callback
            if callback:
                callback(status_data)

            # Check terminal states
            status = status_data.get("status", "unknown")

            if status == "completed":
                return status_data

            if status == "failed":
                error = status_data.get("error", "Unknown error")
                error_details = status_data.get("error_details")
                raise JobFailedException(
                    message=f"Job {job_id} failed: {error}",
                    job_id=job_id,
                    error_details=error_details
                )

            # Wait before next poll
            time.sleep(self.poll_interval)

    def _poll_once(self, job_id: str) -> Dict[str, Any]:
        """
        Poll job status once.

        Args:
            job_id: Job ID to poll

        Returns:
            Job status dictionary
        """
        return self.api_client.get_job_status(job_id)
