"""Jobs resource — /jobs endpoints."""

from __future__ import annotations

import time
from typing import TYPE_CHECKING, Any, Iterator

from curvestone._exceptions import TimeoutError
from curvestone.types.job import Job, JobList

if TYPE_CHECKING:
    from curvestone._client import AsyncCurvestone, Curvestone


class SyncJobs:
    def __init__(self, client: Curvestone) -> None:
        self._client = client

    def retrieve(self, job_id: str) -> Job:
        """Get a single job by ID."""
        resp = self._client._request("GET", f"/jobs/{job_id}")
        return Job.model_validate(resp)

    def list(
        self,
        *,
        status: str | None = None,
        type: str | None = None,
        reference: str | None = None,
        limit: int = 20,
        starting_after: str | None = None,
    ) -> JobList:
        """List jobs with optional filters."""
        resp = self._client._request(
            "GET",
            "/jobs",
            params={
                "status": status,
                "type": type,
                "reference": reference,
                "limit": limit,
                "starting_after": starting_after,
            },
        )
        return JobList.model_validate(resp)

    def cancel(self, job_id: str) -> Job:
        """Cancel a queued or in-progress job."""
        resp = self._client._request("POST", f"/jobs/{job_id}/cancel")
        return Job.model_validate(resp)

    def poll(
        self,
        job_id: str,
        *,
        interval: float = 1.0,
        timeout: float = 300.0,
    ) -> Job:
        """Poll a job until it reaches a terminal state (completed/failed/cancelled)."""
        deadline = time.monotonic() + timeout
        terminal = {"completed", "failed", "cancelled"}

        while True:
            job = self.retrieve(job_id)
            if job.status in terminal:
                return job
            if time.monotonic() >= deadline:
                raise TimeoutError(
                    f"Job {job_id} did not complete within {timeout}s (status: {job.status})"
                )
            time.sleep(interval)

    def stream(self, job_id: str) -> Iterator[dict[str, Any]]:
        """Stream job progress via SSE. Yields event dicts."""
        with self._client._stream_sse(f"/jobs/{job_id}/stream") as event_source:
            for sse in event_source.iter_sse():
                import json

                yield {"event": sse.event, "data": json.loads(sse.data) if sse.data else {}}


class AsyncJobs:
    def __init__(self, client: AsyncCurvestone) -> None:
        self._client = client

    async def retrieve(self, job_id: str) -> Job:
        """Get a single job by ID."""
        resp = await self._client._request("GET", f"/jobs/{job_id}")
        return Job.model_validate(resp)

    async def list(
        self,
        *,
        status: str | None = None,
        type: str | None = None,
        reference: str | None = None,
        limit: int = 20,
        starting_after: str | None = None,
    ) -> JobList:
        """List jobs with optional filters."""
        resp = await self._client._request(
            "GET",
            "/jobs",
            params={
                "status": status,
                "type": type,
                "reference": reference,
                "limit": limit,
                "starting_after": starting_after,
            },
        )
        return JobList.model_validate(resp)

    async def cancel(self, job_id: str) -> Job:
        """Cancel a queued or in-progress job."""
        resp = await self._client._request("POST", f"/jobs/{job_id}/cancel")
        return Job.model_validate(resp)

    async def poll(
        self,
        job_id: str,
        *,
        interval: float = 1.0,
        timeout: float = 300.0,
    ) -> Job:
        """Poll a job until it reaches a terminal state."""
        import asyncio

        deadline = time.monotonic() + timeout
        terminal = {"completed", "failed", "cancelled"}

        while True:
            job = await self.retrieve(job_id)
            if job.status in terminal:
                return job
            if time.monotonic() >= deadline:
                raise TimeoutError(
                    f"Job {job_id} did not complete within {timeout}s (status: {job.status})"
                )
            await asyncio.sleep(interval)

    async def stream(self, job_id: str):
        """Stream job progress via SSE. Yields event dicts."""
        import json

        async with self._client._stream_sse(f"/jobs/{job_id}/stream") as event_source:
            async for sse in event_source.aiter_sse():
                yield {"event": sse.event, "data": json.loads(sse.data) if sse.data else {}}
