"""Backups client for supervisor."""

from collections.abc import AsyncIterator
from typing import Any

from aiohttp import MultipartWriter
from multidict import MultiDict

from .client import _SupervisorComponentClient
from .const import ResponseType
from .models.backups import (
    Backup,
    BackupComplete,
    BackupJob,
    BackupList,
    BackupsInfo,
    BackupsOptions,
    DownloadBackupOptions,
    FreezeOptions,
    FullBackupOptions,
    FullRestoreOptions,
    NewBackup,
    PartialBackupOptions,
    PartialRestoreOptions,
    RemoveBackupOptions,
    UploadBackupOptions,
    UploadedBackup,
)


class BackupsClient(_SupervisorComponentClient):
    """Handles backups access in Supervisor."""

    async def list(self) -> list[Backup]:
        """List backups."""
        result = await self._client.get("backups")
        return BackupList.from_dict(result.data).backups

    async def info(self) -> BackupsInfo:
        """Get backups info."""
        result = await self._client.get("backups/info")
        return BackupsInfo.from_dict(result.data)

    async def set_options(self, options: BackupsOptions) -> None:
        """Set options for backups."""
        await self._client.post("backups/options", json=options.to_dict())

    async def reload(self) -> None:
        """Reload backups cache."""
        await self._client.post("backups/reload")

    async def freeze(self, options: FreezeOptions | None = None) -> None:
        """Start a freeze for external snapshot process."""
        await self._client.post(
            "backups/freeze", json=options.to_dict() if options else None
        )

    async def thaw(self) -> None:
        """Thaw an active freeze when external snapshot process ends."""
        await self._client.post("backups/thaw")

    async def full_backup(self, options: FullBackupOptions | None = None) -> NewBackup:
        """Create a new full backup."""
        # Must disable timeout if API call waits for backup to complete
        kwargs: dict[str, Any] = {}
        if not options or not options.background:
            kwargs["timeout"] = None

        result = await self._client.post(
            "backups/new/full",
            json=options.to_dict() if options else None,
            response_type=ResponseType.JSON,
            **kwargs,
        )
        return NewBackup.from_dict(result.data)

    async def partial_backup(self, options: PartialBackupOptions) -> NewBackup:
        """Create a new partial backup."""
        # Must disable timeout if API call waits for backup to complete
        kwargs: dict[str, Any] = {}
        if not options.background:
            kwargs["timeout"] = None

        result = await self._client.post(
            "backups/new/partial",
            json=options.to_dict(),
            response_type=ResponseType.JSON,
            **kwargs,
        )
        return NewBackup.from_dict(result.data)

    async def backup_info(self, backup: str) -> BackupComplete:
        """Get backup details."""
        result = await self._client.get(f"backups/{backup}/info")
        return BackupComplete.from_dict(result.data)

    async def remove_backup(
        self, backup: str, options: RemoveBackupOptions | None = None
    ) -> None:
        """Remove a backup."""
        await self._client.delete(
            f"backups/{backup}", json=options.to_dict() if options else None
        )

    async def full_restore(
        self, backup: str, options: FullRestoreOptions | None = None
    ) -> BackupJob:
        """Start full restore from backup."""
        # Must disable timeout if API call waits for restore to complete
        kwargs: dict[str, Any] = {}
        if not options or not options.background:
            kwargs["timeout"] = None

        result = await self._client.post(
            f"backups/{backup}/restore/full",
            json=options.to_dict() if options else None,
            response_type=ResponseType.JSON,
            **kwargs,
        )
        return BackupJob.from_dict(result.data)

    async def partial_restore(
        self, backup: str, options: PartialRestoreOptions
    ) -> BackupJob:
        """Start partial restore from backup."""
        # Must disable timeout if API call waits for restore to complete
        kwargs: dict[str, Any] = {}
        if not options.background:
            kwargs["timeout"] = None

        result = await self._client.post(
            f"backups/{backup}/restore/partial",
            json=options.to_dict(),
            response_type=ResponseType.JSON,
            **kwargs,
        )
        return BackupJob.from_dict(result.data)

    async def upload_backup(
        self, stream: AsyncIterator[bytes], options: UploadBackupOptions | None = None
    ) -> str:
        """Upload backup by stream and return slug."""
        params = MultiDict()
        if options:
            if options.location:
                for location in options.location:
                    params.add("location", location)
            if options.filename:
                params.add("filename", options.filename.as_posix())

        with MultipartWriter("form-data") as mp:
            mp.append(stream)
            result = await self._client.post(
                "backups/new/upload",
                params=params,
                data=mp,
                response_type=ResponseType.JSON,
                timeout=None,
            )

        return UploadedBackup.from_dict(result.data).slug

    async def download_backup(
        self, backup: str, options: DownloadBackupOptions | None = None
    ) -> AsyncIterator[bytes]:
        """Download backup and return stream."""
        params = MultiDict()
        if options and options.location:
            params.add("location", options.location)

        result = await self._client.get(
            f"backups/{backup}/download",
            params=params,
            response_type=ResponseType.STREAM,
            timeout=None,
        )
        return result.data
