"""CDN file storage tool using generated API client."""

import logging
from pathlib import Path
from typing import Any, BinaryIO, Union

import httpx

from .._config import SDKConfig

logger = logging.getLogger(__name__)
from .._api.client import (
    BaseResource,
    AsyncBaseResource,
    SyncCdnCdnAPI,
    CdnCdnAPI,
)
from .._api.generated.cdn.cdn__api__cdn.models import (
    CDNFileList,
    CDNFileDetail,
    CDNFileUpload,
    CDNFileUploadRequest,
    PaginatedCDNFileListList,
    CDNStats,
)


class CDNUploadResponse:
    """Response from CDN upload - can be immediate result or async job."""

    def __init__(self, data: dict, is_async: bool = False):
        self._data = data
        self.is_async = is_async

        if is_async:
            self.status = data.get("status")
            self.job_id = data.get("job_id")
            self.message = data.get("message")
            self.uuid = None
            self.filename = None
            self.url = None
        else:
            self.uuid = data.get("uuid")
            self.filename = data.get("filename")
            self.url = data.get("url")
            self.content_type = data.get("content_type")
            self.size_bytes = data.get("size_bytes")
            self.status = "completed"
            self.job_id = None
            self.message = None

    def __repr__(self):
        if self.is_async:
            return f"CDNUploadResponse(status={self.status}, job_id={self.job_id})"
        return f"CDNUploadResponse(uuid={self.uuid}, filename={self.filename})"


class CDNResource(BaseResource):
    """CDN file storage tool (sync).

    Uses generated SyncCdnCdnAPI client.
    """

    def __init__(self, config: SDKConfig):
        super().__init__(config)
        self._api = SyncCdnCdnAPI(self._http_client)

    def upload(
        self,
        file: Path | BinaryIO | bytes | None = None,
        *,
        url: str | None = None,
        filename: str | None = None,
        ttl: str | None = None,
        is_public: bool = True,
        metadata: dict[str, Any] | None = None,
    ) -> Union[CDNFileDetail, CDNUploadResponse]:
        """
        Upload a file to CDN.

        Supports two modes:
        - file: Direct upload from Path, bytes, or file-like object
        - url: Server downloads from URL (may be processed async)

        Note: File upload requires multipart/form-data, so we use httpx directly.
        """
        if not file and not url:
            logger.error("CDN upload failed: neither file nor url provided")
            raise ValueError("Either 'file' or 'url' must be provided")
        if file and url:
            logger.error("CDN upload failed: both file and url provided")
            raise ValueError("Provide either 'file' or 'url', not both")

        data = {"is_public": str(is_public).lower()}
        if ttl:
            data["ttl"] = ttl
        if metadata:
            import json
            data["metadata"] = json.dumps(metadata)

        if url:
            # URL mode - use httpx directly since response varies (async job vs file)
            logger.debug("CDN upload from URL: %s", url)
            json_data = {"url": url, "is_public": is_public}
            if filename is not None:
                json_data["filename"] = filename
            if ttl is not None:
                json_data["ttl"] = ttl
            if metadata is not None:
                json_data["metadata"] = metadata

            response = self._http_client.post("/api/cdn/", json=json_data)
            response.raise_for_status()
            response_data = response.json()

            # Check if async processing
            if response.status_code == 202 or "job_id" in response_data or response_data.get("status") == "processing":
                logger.info("CDN upload started async job: %s", response_data.get("job_id"))
                return CDNUploadResponse(response_data, is_async=True)
            logger.info("CDN upload completed: uuid=%s", response_data.get("uuid"))
            return CDNFileDetail.model_validate(response_data)
        else:
            # File upload mode - use httpx directly (multipart)
            if isinstance(file, Path):
                filename = filename or file.name
                logger.debug("CDN upload from file: %s", file)
                with open(file, "rb") as f:
                    content = f.read()
            elif isinstance(file, bytes):
                content = file
                if not filename:
                    logger.error("CDN upload failed: filename required for bytes")
                    raise ValueError("filename required when uploading bytes")
            else:
                # BinaryIO
                content = file.read()
                filename = filename or getattr(file, "name", "file")

            logger.debug("CDN uploading file: %s (%d bytes)", filename, len(content))
            files = {"file": (filename, content)}
            response = self._http_client.post("/api/cdn/", files=files, data=data)

            response.raise_for_status()
            response_data = response.json()

            if response.status_code == 202 or "job_id" in response_data:
                logger.info("CDN upload started async job: %s", response_data.get("job_id"))
                return CDNUploadResponse(response_data, is_async=True)

            logger.info("CDN upload completed: uuid=%s, size=%d bytes", response_data.get("uuid"), len(content))
            return CDNFileDetail.model_validate(response_data)

    def get(self, uuid: str) -> CDNFileDetail:
        """Get file details by UUID."""
        return self._api.retrieve(uuid)

    def list(
        self,
        *,
        page: int = 1,
        page_size: int = 20,
    ) -> PaginatedCDNFileListList:
        """List files."""
        return self._api.list(page=page, page_size=page_size)

    def delete(self, uuid: str) -> bool:
        """Delete a file by UUID."""
        try:
            logger.debug("CDN deleting file: %s", uuid)
            self._api.destroy(uuid)
            logger.info("CDN file deleted: %s", uuid)
            return True
        except httpx.HTTPStatusError as e:
            logger.warning("CDN delete failed for %s: %s", uuid, e)
            return False

    def stats(self) -> CDNStats:
        """Get storage statistics."""
        return self._api.stats_retrieve()


class AsyncCDNResource(AsyncBaseResource):
    """CDN file storage tool (async).

    Uses generated CdnCdnAPI client.
    """

    def __init__(self, config: SDKConfig):
        super().__init__(config)
        self._api = CdnCdnAPI(self._http_client)

    async def upload(
        self,
        file: Path | BinaryIO | bytes | None = None,
        *,
        url: str | None = None,
        filename: str | None = None,
        ttl: str | None = None,
        is_public: bool = True,
        metadata: dict[str, Any] | None = None,
    ) -> Union[CDNFileDetail, CDNUploadResponse]:
        """
        Upload a file to CDN.

        Note: File upload requires multipart/form-data, so we use httpx directly.
        """
        if not file and not url:
            logger.error("CDN upload failed: neither file nor url provided")
            raise ValueError("Either 'file' or 'url' must be provided")
        if file and url:
            logger.error("CDN upload failed: both file and url provided")
            raise ValueError("Provide either 'file' or 'url', not both")

        data = {"is_public": str(is_public).lower()}
        if ttl:
            data["ttl"] = ttl
        if metadata:
            import json
            data["metadata"] = json.dumps(metadata)

        if url:
            # URL mode - use httpx directly since response varies (async job vs file)
            logger.debug("CDN async upload from URL: %s", url)
            json_data = {"url": url, "is_public": is_public}
            if filename is not None:
                json_data["filename"] = filename
            if ttl is not None:
                json_data["ttl"] = ttl
            if metadata is not None:
                json_data["metadata"] = metadata

            response = await self._http_client.post("/api/cdn/", json=json_data)
            response.raise_for_status()
            response_data = response.json()

            # Check if async processing
            if response.status_code == 202 or "job_id" in response_data or response_data.get("status") == "processing":
                logger.info("CDN upload started async job: %s", response_data.get("job_id"))
                return CDNUploadResponse(response_data, is_async=True)
            logger.info("CDN upload completed: uuid=%s", response_data.get("uuid"))
            return CDNFileDetail.model_validate(response_data)
        else:
            # File upload mode - use httpx directly (multipart)
            if isinstance(file, Path):
                filename = filename or file.name
                logger.debug("CDN async upload from file: %s", file)
                with open(file, "rb") as f:
                    content = f.read()
            elif isinstance(file, bytes):
                content = file
                if not filename:
                    logger.error("CDN upload failed: filename required for bytes")
                    raise ValueError("filename required when uploading bytes")
            else:
                # BinaryIO
                content = file.read()
                filename = filename or getattr(file, "name", "file")

            logger.debug("CDN async uploading file: %s (%d bytes)", filename, len(content))
            files = {"file": (filename, content)}
            response = await self._http_client.post("/api/cdn/", files=files, data=data)

            response.raise_for_status()
            response_data = response.json()

            if response.status_code == 202 or "job_id" in response_data:
                logger.info("CDN upload started async job: %s", response_data.get("job_id"))
                return CDNUploadResponse(response_data, is_async=True)

            logger.info("CDN upload completed: uuid=%s, size=%d bytes", response_data.get("uuid"), len(content))
            return CDNFileDetail.model_validate(response_data)

    async def get(self, uuid: str) -> CDNFileDetail:
        """Get file details by UUID."""
        return await self._api.retrieve(uuid)

    async def list(
        self,
        *,
        page: int = 1,
        page_size: int = 20,
    ) -> PaginatedCDNFileListList:
        """List files."""
        return await self._api.list(page=page, page_size=page_size)

    async def delete(self, uuid: str) -> bool:
        """Delete a file by UUID."""
        try:
            logger.debug("CDN async deleting file: %s", uuid)
            await self._api.destroy(uuid)
            logger.info("CDN file deleted: %s", uuid)
            return True
        except httpx.HTTPStatusError as e:
            logger.warning("CDN delete failed for %s: %s", uuid, e)
            return False

    async def stats(self) -> CDNStats:
        """Get storage statistics."""
        return await self._api.stats_retrieve()


__all__ = [
    "CDNResource",
    "AsyncCDNResource",
    "CDNUploadResponse",
    "CDNStats",
    # Models
    "CDNFileList",
    "CDNFileDetail",
    "CDNFileUpload",
    "CDNFileUploadRequest",
    "PaginatedCDNFileListList",
]
