"""
DeltaCAT REST client implementation.

Provides the ``DeltaCATClient`` class with type-safe methods for job
lifecycle, data access planning, and credential management.
"""

from __future__ import annotations

import logging
import threading
import time
from typing import Any, Dict, List, Optional

from deltacat_client.models import ClaimedJob, JobStatus

logger = logging.getLogger(__name__)


class DeltaCATClient:
    """REST client for the DeltaCAT API server."""

    def __init__(
        self,
        server_url: str = "http://localhost:8080",
        worker_id: Optional[str] = None,
        timeout: int = 30,
    ):
        import uuid

        self._server = server_url.rstrip("/")
        self._worker_id = worker_id or f"worker-{uuid.uuid4().hex[:8]}"
        self._timeout = timeout
        self._heartbeat_thread: Optional[threading.Thread] = None
        self._current_job: Optional[ClaimedJob] = None

    @property
    def worker_id(self) -> str:
        return self._worker_id

    @property
    def server_url(self) -> str:
        return self._server

    def claim_job(self) -> Optional[ClaimedJob]:
        import requests

        r = requests.get(
            f"{self._server}/v1/jobs/next",
            params={"worker_id": self._worker_id},
            timeout=self._timeout,
        )
        if r.status_code == 204:
            return None
        r.raise_for_status()

        job = ClaimedJob.from_response(r.json())
        self._current_job = job
        return job

    def heartbeat(
        self,
        job_id: str,
        progress: Optional[float] = None,
        message: Optional[str] = None,
    ) -> Dict[str, Any]:
        import requests

        r = requests.post(
            f"{self._server}/v1/jobs/{job_id}/heartbeat",
            json={
                "worker_id": self._worker_id,
                "progress": progress,
                "message": message,
            },
            timeout=self._timeout,
        )
        r.raise_for_status()
        data = r.json()

        if data.get("credentials") and self._current_job:
            self._current_job.credentials = data["credentials"]
            logger.debug("Credentials refreshed for job %s", job_id)

        return data

    def complete(
        self,
        job_id: str,
        records_processed: int = 0,
        output_manifest: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        import requests

        self._stop_heartbeat()

        r = requests.post(
            f"{self._server}/v1/jobs/{job_id}/complete",
            json={
                "worker_id": self._worker_id,
                "records_processed": records_processed,
                "output_manifest": output_manifest,
            },
            timeout=self._timeout,
        )
        r.raise_for_status()
        self._current_job = None
        return r.json()

    def fail(
        self,
        job_id: str,
        error: str,
        retryable: bool = True,
    ) -> Dict[str, Any]:
        import requests

        self._stop_heartbeat()

        r = requests.post(
            f"{self._server}/v1/jobs/{job_id}/fail",
            json={
                "worker_id": self._worker_id,
                "error": error,
                "retryable": retryable,
            },
            timeout=self._timeout,
        )
        r.raise_for_status()
        self._current_job = None
        return r.json()

    def get_job(self, job_id: str) -> JobStatus:
        import requests

        r = requests.get(f"{self._server}/v1/jobs/{job_id}", timeout=self._timeout)
        r.raise_for_status()
        return JobStatus.from_response(r.json())

    def list_jobs(
        self,
        state: Optional[str] = None,
        subscriber_id: Optional[str] = None,
    ) -> List[JobStatus]:
        import requests

        params = {}
        if state:
            params["state"] = state
        if subscriber_id:
            params["subscriber_id"] = subscriber_id

        r = requests.get(f"{self._server}/v1/jobs", params=params, timeout=self._timeout)
        r.raise_for_status()
        return [JobStatus.from_response(j) for j in r.json().get("jobs", [])]

    def start_heartbeat(
        self,
        job_id: str,
        interval_seconds: float = 60.0,
        progress_fn=None,
    ) -> None:
        self._stop_heartbeat()

        def _heartbeat_loop():
            while self._heartbeat_thread is not None:
                try:
                    progress = None
                    message = None
                    if progress_fn:
                        progress, message = progress_fn()
                    self.heartbeat(job_id, progress=progress, message=message)
                except Exception as e:
                    logger.warning("Heartbeat failed for %s: %s", job_id, e)
                time.sleep(interval_seconds)

        self._heartbeat_thread = threading.Thread(target=_heartbeat_loop, daemon=True)
        self._heartbeat_thread.start()

    def _stop_heartbeat(self) -> None:
        if self._heartbeat_thread is not None:
            thread = self._heartbeat_thread
            self._heartbeat_thread = None
            thread.join(timeout=5)
