"""
Stimuli Resource

CRUD operations for QTI stimuli.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any
from urllib.parse import quote

from timeback_common import (
    APIError,
    Paginator,
    serialize_input,
    validate_non_empty_string,
    validate_page_list_params,
    validate_with_schema,
)

from ..types.inputs import CreateStimulusInput, ListParams, UpdateStimulusInput
from ..types.responses import DeleteResponse, ListResponse, Stimulus

if TYPE_CHECKING:
    from ..lib.transport import Transport


class StimuliResource:
    """Stimuli resource for managing QTI stimulus materials."""

    def __init__(self, transport: Transport) -> None:
        self._transport = transport

    @property
    def _base_path(self) -> str:
        return f"{self._transport.paths.base}/stimuli"

    async def list(
        self, params: ListParams | dict[str, Any] | None = None
    ) -> ListResponse[Stimulus]:
        """List stimuli with pagination."""
        query_params: dict[str, Any] = {}
        if params is not None:
            p = serialize_input(params, ListParams) if not isinstance(params, dict) else params
            validate_page_list_params(page=p.get("page"), limit=p.get("limit"))
            for key in ("page", "limit", "sort", "order"):
                if p.get(key) is not None:
                    query_params[key] = p[key]

        body = await self._transport.get(self._base_path, params=query_params or None)
        return ListResponse[Stimulus].model_validate(body)

    def stream(
        self,
        *,
        limit: int = 100,
        sort: str | None = None,
        order: str | None = None,
        max_items: int | None = None,
    ) -> Paginator[dict[str, Any]]:
        """
        Stream stimuli with automatic pagination.

        Args:
            limit: Items per page (default: 100)
            sort: Sort field
            order: Sort order ("asc" or "desc")
            max_items: Maximum total items to fetch

        Returns:
            Async iterator over raw stimulus dicts

        Example:
            ```python
            async for stim in client.stimuli.stream():
                print(stim["identifier"])
            ```
        """
        validate_page_list_params(limit=limit, max_items=max_items)
        params: dict[str, Any] = {}
        if sort:
            params["sort"] = sort
        if order:
            params["order"] = order

        return Paginator(
            self._transport,
            self._base_path,
            unwrap_key="items",
            params=params,
            limit=limit,
            max_items=max_items,
            pagination_style="page",
        )

    async def get(self, identifier: str) -> Stimulus:
        """Get a single stimulus by identifier."""
        validate_non_empty_string(identifier, "identifier")
        body = await self._transport.get(f"{self._base_path}/{quote(identifier, safe='')}")
        return Stimulus.model_validate(body)

    async def create(self, body: CreateStimulusInput | dict[str, Any]) -> Stimulus:
        """Create a new stimulus."""
        validate_with_schema(CreateStimulusInput, body, "stimulus")
        result = await self._transport.post(
            self._base_path, serialize_input(body, CreateStimulusInput)
        )
        return Stimulus.model_validate(result)

    async def update(self, identifier: str, body: UpdateStimulusInput | dict[str, Any]) -> Stimulus:
        """Update a stimulus."""
        validate_non_empty_string(identifier, "identifier")
        validate_with_schema(UpdateStimulusInput, body, "stimulus update")
        payload = serialize_input(body, UpdateStimulusInput)
        payload["identifier"] = identifier
        result = await self._transport.put(
            f"{self._base_path}/{quote(identifier, safe='')}",
            payload,
        )
        return Stimulus.model_validate(result)

    async def upsert(self, identifier: str, body: UpdateStimulusInput | dict[str, Any]) -> Stimulus:
        """
        Create or update a stimulus.

        Attempts an update; if the stimulus doesn't exist (404), creates it.
        """
        validate_non_empty_string(identifier, "identifier")
        try:
            return await self.update(identifier, body)
        except APIError as error:
            if error.status_code == 404:
                data = serialize_input(body, UpdateStimulusInput)
                data["identifier"] = identifier
                validate_with_schema(CreateStimulusInput, data, "stimulus")
                result = await self._transport.post(self._base_path, data)
                return Stimulus.model_validate(result)
            raise

    async def delete(self, identifier: str) -> DeleteResponse:
        """Delete a stimulus."""
        validate_non_empty_string(identifier, "identifier")
        body = await self._transport.delete(f"{self._base_path}/{quote(identifier, safe='')}")
        if body is None:
            return DeleteResponse()
        return DeleteResponse.model_validate(body)
