"""
CFPackages Resource

CASE Framework Packages — complete bundles of documents, items, and associations.
"""

from __future__ import annotations

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

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

from ..types.inputs import CFPackageInput
from ..types.responses import CFPackage, CFPackageUploadResult, CFPackageWithGroups

if TYPE_CHECKING:
    from ..lib.transport import Transport


class PackagesResource:
    """CASE Framework Packages resource."""

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

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

    async def create(self, input_data: CFPackageInput | dict[str, Any]) -> CFPackageUploadResult:
        """
        Upload a complete CASE package.

        Args:
            input_data: The package to upload (document, items, and associations)

        Returns:
            Upload result with document ID and stats
        """
        validate_with_schema(CFPackageInput, input_data, "CASE package")
        payload = serialize_input(input_data, CFPackageInput)
        result = await self._transport.post(self._base_path, payload)
        return CFPackageUploadResult.model_validate(result)

    async def update(
        self, sourced_id: str, input_data: CFPackageInput | dict[str, Any]
    ) -> CFPackageUploadResult:
        """
        Replace a CASE package by sourcedId.

        Args:
            sourced_id: The package's document sourcedId
            input_data: The replacement package

        Returns:
            Upload result with document ID and stats
        """
        validate_non_empty_string(sourced_id, "sourcedId")
        validate_with_schema(CFPackageInput, input_data, "CASE package")
        payload = serialize_input(input_data, CFPackageInput)
        result = await self._transport.put(
            f"{self._base_path}/{quote(sourced_id, safe='')}",
            payload,
        )
        return CFPackageUploadResult.model_validate(result)

    async def upsert(
        self, sourced_id: str, input_data: CFPackageInput | dict[str, Any]
    ) -> CFPackageUploadResult:
        """
        Create or replace a CASE package.

        Attempts an update; if the package doesn't exist (404), creates it.

        Args:
            sourced_id: The package's document sourcedId
            input_data: The package to upload

        Returns:
            Upload result with document ID and stats
        """
        validate_non_empty_string(sourced_id, "sourcedId")
        try:
            return await self.update(sourced_id, input_data)
        except APIError as error:
            if error.status_code == 404:
                return await self.create(input_data)
            raise

    async def get(self, sourced_id: str) -> dict[str, CFPackage]:
        """
        Get a CASE package by sourcedId.

        Args:
            sourced_id: The package's document sourcedId

        Returns:
            Dict with CFPackage key containing the complete package
        """
        validate_non_empty_string(sourced_id, "sourcedId")
        result = await self._transport.get(f"{self._base_path}/{quote(sourced_id, safe='')}")
        return {"CFPackage": CFPackage.model_validate(result.get("CFPackage", result))}

    async def get_groups(self, sourced_id: str) -> dict[str, CFPackageWithGroups]:
        """
        Get a CASE package with hierarchical group structure.

        Args:
            sourced_id: The package's document sourcedId

        Returns:
            Dict with CFPackageWithGroups key containing the grouped package
        """
        validate_non_empty_string(sourced_id, "sourcedId")
        result = await self._transport.get(f"{self._base_path}/{quote(sourced_id, safe='')}/groups")
        return {
            "CFPackageWithGroups": CFPackageWithGroups.model_validate(
                result.get("CFPackageWithGroups", result)
            )
        }
