"""Public /v1 jobs."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from ...models.v1.jobs import (
    ChunkSummary,
    EntityFailure,
    ExtractIframesBacklog,
    FramePrediction,
    InferFramesRealtimeBacklog,
    InferFramesRealtimeValidation,
    JobDetail,
    JobProgress,
    JobSummary,
    SubmitPreview,
    SubmitResult,
    WriteResult,
)
from ...models.v1.pagination import Page, Pagination

if TYPE_CHECKING:
    from ..._http import HttpTransport


class JobsResource:
    """client.jobs — list, get, submit, retry, cancel."""

    def __init__(self, http: HttpTransport) -> None:
        self._http = http

    def list(
        self,
        *,
        type: str | None = None,
        status: str | None = None,
        page_size: int = 50,
        cursor: str | None = None,
    ) -> Page[JobSummary]:
        params: dict[str, Any] = {"page_size": page_size}
        if cursor is not None:
            params["cursor"] = cursor
        if type is not None:
            params["type"] = type
        if status is not None:
            params["status"] = status

        data = self._http.get("/jobs", params=params)
        items = [JobSummary.model_validate(i) for i in data.get("data", [])]
        pagination = Pagination.model_validate(data.get("pagination", {}) or {})
        return Page[JobSummary](
            data=items, pagination=pagination, request_id=data.get("request_id")
        )

    def get(self, job_id: str) -> JobDetail:
        data = self._http.get(f"/jobs/{job_id}")
        return JobDetail.model_validate(data.get("data", {}))

    def progress(self, job_id: str) -> JobProgress:
        data = self._http.get(f"/jobs/{job_id}/progress")
        return JobProgress.model_validate(data.get("data", {}))

    def chunks(
        self,
        job_id: str,
        *,
        status: str | None = None,
        page_size: int = 50,
        cursor: str | None = None,
    ) -> Page[ChunkSummary]:
        params: dict[str, Any] = {"page_size": page_size}
        if cursor is not None:
            params["cursor"] = cursor
        if status is not None:
            params["status"] = status

        data = self._http.get(f"/jobs/{job_id}/chunks", params=params)
        items = [ChunkSummary.model_validate(i) for i in data.get("data", [])]
        pagination = Pagination.model_validate(data.get("pagination", {}) or {})
        return Page[ChunkSummary](
            data=items, pagination=pagination, request_id=data.get("request_id")
        )

    def failures(
        self,
        job_id: str,
        *,
        resolved: bool | None = None,
        page_size: int = 100,
        cursor: str | None = None,
    ) -> Page[EntityFailure]:
        params: dict[str, Any] = {"page_size": page_size}
        if cursor is not None:
            params["cursor"] = cursor
        if resolved is not None:
            params["resolved"] = resolved

        data = self._http.get(f"/jobs/{job_id}/failures", params=params)
        items = [EntityFailure.model_validate(i) for i in data.get("data", [])]
        pagination = Pagination.model_validate(data.get("pagination", {}) or {})
        return Page[EntityFailure](
            data=items,
            pagination=pagination,
            request_id=data.get("request_id"),
        )

    def predictions(
        self,
        job_id: str,
        *,
        page_size: int = 100,
        cursor: str | None = None,
    ) -> Page[FramePrediction]:
        params: dict[str, Any] = {"page_size": page_size}
        if cursor is not None:
            params["cursor"] = cursor

        data = self._http.get(f"/jobs/{job_id}/predictions", params=params)
        items = [FramePrediction.model_validate(i) for i in data.get("data", [])]
        pagination = Pagination.model_validate(data.get("pagination", {}) or {})
        return Page[FramePrediction](
            data=items,
            pagination=pagination,
            request_id=data.get("request_id"),
        )

    def submit_embed(
        self,
        *,
        collection_id: str | None = None,
        factory_id: str | None = None,
        model_id: str | None = None,
        entity_type: str | None = None,
    ) -> JobDetail:
        payload: dict[str, Any] = {}
        if collection_id is not None:
            payload["collection_id"] = collection_id
        if factory_id is not None:
            payload["factory_id"] = factory_id
        if model_id is not None:
            payload["model_id"] = model_id
        if entity_type is not None:
            payload["entity_type"] = entity_type
        data = self._http.post("/jobs/embed", json=payload)
        return JobDetail.model_validate(data.get("data", {}))

    def submit_frame_embed(
        self,
        *,
        selector: dict[str, Any],
        model_id: str | None = None,
        task_type: str | None = None,
        output_dimensionality: int | None = None,
        requests_per_minute: int | None = None,
        chunk_size: int | None = None,
    ) -> JobDetail:
        payload: dict[str, Any] = {"selector": selector}
        if model_id is not None:
            payload["model_id"] = model_id
        if task_type is not None:
            payload["task_type"] = task_type
        if output_dimensionality is not None:
            payload["output_dimensionality"] = output_dimensionality
        if requests_per_minute is not None:
            payload["requests_per_minute"] = requests_per_minute
        if chunk_size is not None:
            payload["chunk_size"] = chunk_size
        data = self._http.post("/jobs/frame-embed", json=payload)
        return JobDetail.model_validate(data.get("data", {}))

    def submit_project(
        self,
        embedding_model_id: str,
        *,
        factory_ids: list[str] | None = None,
    ) -> JobDetail:
        payload: dict[str, Any] = {"embedding_model_id": embedding_model_id}
        if factory_ids is not None:
            payload["factory_ids"] = factory_ids
        data = self._http.post("/jobs/project", json=payload)
        return JobDetail.model_validate(data.get("data", {}))

    def submit_extract_iframes(
        self,
        *,
        factory_id: str | None = None,
        collection_id: str | None = None,
        worker_ids: list[str] | None = None,
        limit: int | None = None,
        chunk_size: int | None = None,
        frame_extraction_timeout_sec: int | None = None,
        num_frames: int | None = None,
        frame_height: int | None = None,
        frame_jpeg_quality: int | None = None,
        max_downloads_per_worker: int | None = None,
        max_clips_in_flight: int | None = None,
        max_seek_ffmpeg_processes_per_clip: int | None = None,
        max_uploads_per_clip: int | None = None,
        ffmpeg_threads_per_process: int | None = None,
    ) -> JobDetail:
        selector: dict[str, Any] = {}
        if factory_id is not None:
            selector["factory_id"] = factory_id
        if collection_id is not None:
            selector["collection_id"] = collection_id
        if worker_ids is not None:
            selector["worker_ids"] = worker_ids
        payload: dict[str, Any] = {"selector": selector}
        if limit is not None:
            payload["selector"]["limit"] = limit
        optional_fields = {
            "chunk_size": chunk_size,
            "frame_extraction_timeout_sec": frame_extraction_timeout_sec,
            "num_frames": num_frames,
            "frame_height": frame_height,
            "frame_jpeg_quality": frame_jpeg_quality,
            "max_downloads_per_worker": max_downloads_per_worker,
            "max_clips_in_flight": max_clips_in_flight,
            "max_seek_ffmpeg_processes_per_clip": max_seek_ffmpeg_processes_per_clip,
            "max_uploads_per_clip": max_uploads_per_clip,
            "ffmpeg_threads_per_process": ffmpeg_threads_per_process,
        }
        payload.update({k: v for k, v in optional_fields.items() if v is not None})
        data = self._http.post("/jobs/extract-iframes", json=payload)
        return JobDetail.model_validate(data.get("data", {}))

    def extract_iframes_backlog(
        self,
        *,
        factory_id: str | None = None,
        collection_id: str | None = None,
    ) -> ExtractIframesBacklog:
        params: dict[str, Any] = {}
        if factory_id is not None:
            params["factory_id"] = factory_id
        if collection_id is not None:
            params["collection_id"] = collection_id
        data = self._http.get("/jobs/extract-iframes/backlog", params=params)
        return ExtractIframesBacklog.model_validate(data.get("data", {}))

    def submit_extract_audio(
        self,
        *,
        factory_id: str | None = None,
        limit: int | None = None,
        chunk_size: int | None = None,
    ) -> JobDetail:
        payload: dict[str, Any] = {}
        if factory_id is not None:
            payload["factory_id"] = factory_id
        if limit is not None:
            payload["limit"] = limit
        if chunk_size is not None:
            payload["chunk_size"] = chunk_size
        data = self._http.post("/jobs/extract-audio", json=payload)
        return JobDetail.model_validate(data.get("data", {}))

    def validate_infer_frames_rt(
        self,
        *,
        selector: dict[str, Any],
        model: str,
        prompt_text: str,
        response_json_schema: dict[str, Any],
        system_instruction: str | None = None,
        response_mime_type: str | None = None,
        temperature: float | None = None,
        top_p: float | None = None,
        top_k: int | None = None,
        max_output_tokens: int | None = None,
        candidate_count: int | None = None,
        seed: int | None = None,
        media_resolution: str | None = None,
        thinking_budget: int | None = None,
        include_thoughts: bool | None = None,
        batch_size: int | None = None,
        requests_per_minute: int | None = None,
        permit_lease_size: int | None = None,
        max_parallel_requests_per_worker: int | None = None,
        max_attempts_per_request: int | None = None,
        asset_type_id: str | None = None,
        preferred_platform: str | None = None,
        preferred_region: str | None = None,
        chunk_size: int | None = None,
    ) -> InferFramesRealtimeValidation:
        payload: dict[str, Any] = {
            "selector": selector,
            "model": model,
            "prompt_text": prompt_text,
            "response_json_schema": response_json_schema,
        }
        optional_fields = {
            "system_instruction": system_instruction,
            "response_mime_type": response_mime_type,
            "temperature": temperature,
            "top_p": top_p,
            "top_k": top_k,
            "max_output_tokens": max_output_tokens,
            "candidate_count": candidate_count,
            "seed": seed,
            "media_resolution": media_resolution,
            "thinking_budget": thinking_budget,
            "include_thoughts": include_thoughts,
            "batch_size": batch_size,
            "requests_per_minute": requests_per_minute,
            "permit_lease_size": permit_lease_size,
            "max_parallel_requests_per_worker": max_parallel_requests_per_worker,
            "max_attempts_per_request": max_attempts_per_request,
            "asset_type_id": asset_type_id,
            "preferred_platform": preferred_platform,
            "preferred_region": preferred_region,
            "chunk_size": chunk_size,
        }
        payload.update({k: v for k, v in optional_fields.items() if v is not None})
        data = self._http.post("/jobs/infer-frames-rt/validate", json=payload)
        return InferFramesRealtimeValidation.model_validate(data.get("data", {}))

    def submit_infer_frames_rt(
        self,
        *,
        selector: dict[str, Any],
        model: str,
        prompt_text: str,
        response_json_schema: dict[str, Any],
        system_instruction: str | None = None,
        response_mime_type: str | None = None,
        temperature: float | None = None,
        top_p: float | None = None,
        top_k: int | None = None,
        max_output_tokens: int | None = None,
        candidate_count: int | None = None,
        seed: int | None = None,
        media_resolution: str | None = None,
        thinking_budget: int | None = None,
        include_thoughts: bool | None = None,
        batch_size: int | None = None,
        requests_per_minute: int | None = None,
        permit_lease_size: int | None = None,
        max_parallel_requests_per_worker: int | None = None,
        max_attempts_per_request: int | None = None,
        asset_type_id: str | None = None,
        preferred_platform: str | None = None,
        preferred_region: str | None = None,
        chunk_size: int | None = None,
    ) -> JobDetail:
        payload: dict[str, Any] = {
            "selector": selector,
            "model": model,
            "prompt_text": prompt_text,
            "response_json_schema": response_json_schema,
        }
        optional_fields = {
            "system_instruction": system_instruction,
            "response_mime_type": response_mime_type,
            "temperature": temperature,
            "top_p": top_p,
            "top_k": top_k,
            "max_output_tokens": max_output_tokens,
            "candidate_count": candidate_count,
            "seed": seed,
            "media_resolution": media_resolution,
            "thinking_budget": thinking_budget,
            "include_thoughts": include_thoughts,
            "batch_size": batch_size,
            "requests_per_minute": requests_per_minute,
            "permit_lease_size": permit_lease_size,
            "max_parallel_requests_per_worker": max_parallel_requests_per_worker,
            "max_attempts_per_request": max_attempts_per_request,
            "asset_type_id": asset_type_id,
            "preferred_platform": preferred_platform,
            "preferred_region": preferred_region,
            "chunk_size": chunk_size,
        }
        payload.update({k: v for k, v in optional_fields.items() if v is not None})
        data = self._http.post("/jobs/infer-frames-rt", json=payload)
        return JobDetail.model_validate(data.get("data", {}))

    def infer_frames_rt_backlog(
        self,
        *,
        factory_id: str | None = None,
        collection_id: str | None = None,
        asset_type_id: str | None = None,
    ) -> InferFramesRealtimeBacklog:
        params: dict[str, Any] = {}
        if factory_id is not None:
            params["factory_id"] = factory_id
        if collection_id is not None:
            params["collection_id"] = collection_id
        if asset_type_id is not None:
            params["asset_type_id"] = asset_type_id
        data = self._http.get("/jobs/infer-frames-rt/backlog", params=params)
        return InferFramesRealtimeBacklog.model_validate(data.get("data", {}))

    def submit_hf_upload(self, collection_id: str) -> JobDetail:
        data = self._http.post("/jobs/hf-upload", json={"collection_id": collection_id})
        return JobDetail.model_validate(data.get("data", {}))

    def submit_hf_exclusion(self, collection_id: str) -> JobDetail:
        data = self._http.post("/jobs/hf-exclusion", json={"collection_id": collection_id})
        return JobDetail.model_validate(data.get("data", {}))

    def retry(self, job_id: str) -> WriteResult:
        data = self._http.post(f"/jobs/{job_id}/retry", json={})
        return WriteResult.model_validate(data)

    def cancel(self, job_id: str) -> WriteResult:
        data = self._http.post(f"/jobs/{job_id}/cancel", json={})
        return WriteResult.model_validate(data)

    def submit(
        self,
        job_id: str,
        *,
        max_tasks: int | None = None,
        spot: bool | None = None,
        platform: str | None = None,
        image: str | None = None,
        region: str | None = None,
        cpu: str | None = None,
        memory: str | None = None,
    ) -> SubmitResult:
        payload: dict[str, Any] = {}
        optional_fields = {
            "max_tasks": max_tasks,
            "spot": spot,
            "platform": platform,
            "image": image,
            "region": region,
            "cpu": cpu,
            "memory": memory,
        }
        payload.update({k: v for k, v in optional_fields.items() if v is not None})
        data = self._http.post(f"/jobs/{job_id}/submit", json=payload)
        return SubmitResult.model_validate(data.get("data", {}))

    def submit_preview(
        self,
        job_id: str,
        *,
        max_tasks: int | None = None,
        spot: bool | None = None,
    ) -> SubmitPreview:
        params: dict[str, Any] = {}
        if max_tasks is not None:
            params["max_tasks"] = max_tasks
        if spot is not None:
            params["spot"] = spot
        data = self._http.get(f"/jobs/{job_id}/submit/preview", params=params)
        return SubmitPreview.model_validate(data.get("data", {}))

    def iter(self, *, type: str | None = None, status: str | None = None, page_size: int = 50):
        from ...models.v1.pagination import auto_paginate

        return auto_paginate(self.list, type=type, status=status, page_size=page_size)
