"""Facade JsonRpcClient combining transport and feature mixins.

Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com
"""

from __future__ import annotations

from typing import Any
from typing import Callable
from typing import Optional

from mcp_proxy_adapter.client.jsonrpc_client.command_api import CommandApiMixin
from mcp_proxy_adapter.client.jsonrpc_client.exceptions import TransferClientError
from mcp_proxy_adapter.client.jsonrpc_client.proxy_api import ProxyApiMixin
from mcp_proxy_adapter.client.jsonrpc_client.queue_api import QueueApiMixin
from mcp_proxy_adapter.transfer.client_downloader import TransferDownloader
from mcp_proxy_adapter.transfer.client_uploader import TransferUploader
from mcp_proxy_adapter.transfer.models import TransferDownloadReceipt
from mcp_proxy_adapter.transfer.models import TransferReceipt

DEFAULT_TRANSFER_CHUNK_SIZE = 1048576
DEFAULT_TRANSFER_CHECKSUM_ALGORITHM = "sha256"


class JsonRpcClient(ProxyApiMixin, QueueApiMixin, CommandApiMixin):
    """High-level asynchronous JSON-RPC client facade."""

    def __init__(
        self,
        protocol: str = "http",
        host: str = "127.0.0.1",
        port: int = 8080,
        token_header: Optional[str] = None,
        token: Optional[str] = None,
        cert: Optional[str] = None,
        key: Optional[str] = None,
        ca: Optional[str] = None,
        check_hostname: bool = False,
        timeout: Optional[float] = None,
    ) -> None:
        """
        Initialize JSON-RPC client.

        Args:
            protocol: Transport protocol (http, https, mtls)
            host: Server hostname
            port: Server port
            token_header: Header name for authentication token
            token: Authentication token value
            cert: Path to client certificate file
            key: Path to client private key file
            ca: Path to CA certificate file
            check_hostname: Whether to verify hostname in SSL connections
            timeout: HTTP client timeout in seconds. If None, uses value from
                MCP_PROXY_ADAPTER_HTTP_TIMEOUT environment variable, or defaults to 30.0.
                This timeout applies to all HTTP requests including status polling.
        """
        super().__init__(
            protocol=protocol,
            host=host,
            port=port,
            token_header=token_header,
            token=token,
            cert=cert,
            key=key,
            ca=ca,
            check_hostname=check_hostname,
            timeout=timeout,
        )
        self._transfer_uploader: TransferUploader | None = None
        self._transfer_downloader: TransferDownloader | None = None

    def _get_transfer_uploader(self) -> TransferUploader:
        """Lazily create the uploader bound to this transport."""
        if self._transfer_uploader is None:
            self._transfer_uploader = TransferUploader(
                self,
                default_chunk_size=DEFAULT_TRANSFER_CHUNK_SIZE,
                checksum_algorithm=DEFAULT_TRANSFER_CHECKSUM_ALGORITHM,
                compression="identity",
            )
        return self._transfer_uploader

    def _get_transfer_downloader(self) -> TransferDownloader:
        """Lazily create the downloader bound to this transport."""
        if self._transfer_downloader is None:
            self._transfer_downloader = TransferDownloader(
                self,
                default_chunk_size=DEFAULT_TRANSFER_CHUNK_SIZE,
                checksum_algorithm=DEFAULT_TRANSFER_CHECKSUM_ALGORITHM,
            )
        return self._transfer_downloader

    async def upload_file(
        self,
        source_path: str,
        filename: str | None = None,
        compression: str = "identity",
        chunk_size: int | None = None,
        on_progress: Optional[Callable[[int, int], None]] = None,
    ) -> TransferReceipt:
        """Upload a file using transfer HTTP routes."""
        return await self._get_transfer_uploader().upload_file(
            source_path,
            filename=filename,
            compression=compression,
            chunk_size=chunk_size,
            on_progress=on_progress,
        )

    async def resume_upload(
        self,
        source_path: str,
        transfer_id: str,
        chunk_size: int | None = None,
    ) -> TransferReceipt:
        """Resume an existing file upload."""
        return await self._get_transfer_uploader().resume_upload(
            source_path,
            transfer_id,
            chunk_size=chunk_size,
        )

    async def download_file(
        self,
        transfer_id: str,
        destination_path: str,
        chunk_size: int | None = None,
        on_progress: Optional[Callable[[int, int], None]] = None,
    ) -> TransferDownloadReceipt:
        """Download a file using transfer HTTP routes."""
        return await self._get_transfer_downloader().download_file(
            transfer_id,
            destination_path,
            chunk_size=chunk_size,
            on_progress=on_progress,
        )

    async def resume_download(
        self,
        transfer_id: str,
        destination_path: str,
        chunk_size: int | None = None,
    ) -> TransferDownloadReceipt:
        """Resume a partially completed download."""
        return await self._get_transfer_downloader().resume_download(
            transfer_id,
            destination_path,
            chunk_size=chunk_size,
        )

    async def execute_command_with_file(
        self,
        command_name: str,
        params: dict[str, object],
        source_path: str,
        filename: str | None = None,
        compression: str = "identity",
        chunk_size: int | None = None,
        auto_poll: bool = True,
        timeout: float | None = None,
    ) -> dict[str, Any]:
        """Upload a file then dispatch a normal command with transfer metadata."""
        receipt = await self.upload_file(
            source_path,
            filename=filename,
            compression=compression,
            chunk_size=chunk_size,
        )
        if receipt.completed is not True:
            raise TransferClientError(
                "upload did not complete before command dispatch",
                details={"source_path": source_path},
            )

        merged: dict[str, object] = dict(params)
        merged["transfer_id"] = receipt.transfer_id
        merged["filename"] = receipt.filename
        merged["compression"] = receipt.compression
        merged["checksum_algorithm"] = receipt.checksum_algorithm
        return await self.execute_command_unified(
            command_name,
            merged,
            auto_poll=auto_poll,
            timeout=timeout,
        )


__all__ = ["JsonRpcClient"]
