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

JSON-RPC command: start a download session (REST parity: POST /api/transfer/downloads).
"""

from __future__ import annotations

from typing import Any, Dict

from mcp_proxy_adapter.api.transfer_session_service import (
    TransferPayloadValidationError,
    build_transfer_chunk_transport,
    run_create_download_session,
)
from mcp_proxy_adapter.commands.base import Command, CommandResult
from mcp_proxy_adapter.commands.transfer_command_support import (
    transfer_domain_error_result,
    transfer_validation_error_result,
)
from mcp_proxy_adapter.transfer import TransferCompressionError
from mcp_proxy_adapter.transfer import TransferError
from mcp_proxy_adapter.transfer import TransferTooLargeError


class TransferDownloadBeginResult(CommandResult):
    """Download session receipt plus GET chunk template."""

    def __init__(self, data: dict[str, object]) -> None:
        super().__init__(success=True, data=dict(data), error="")

    @classmethod
    def get_schema(cls) -> Dict[str, Any]:
        transport_props = {
            "chunk_method": {
                "type": "string",
                "description": "HTTP method for chunk reads (GET).",
            },
            "chunk_path_template": {
                "type": "string",
                "description": (
                    "Relative path with {transfer_id}, {offset}, {limit} query placeholders."
                ),
            },
            "protocol_hint": {
                "type": "string",
                "description": "same_as_jsonrpc: reuse JSON-RPC origin and auth.",
            },
        }
        data_props = {
            "transfer_id": {"type": "string", "description": "Download session id."},
            "filename": {"type": "string", "description": "Suggested filename."},
            "size_bytes": {
                "type": "integer",
                "description": "Wire size (compressed if gzip).",
            },
            "checksum_algorithm": {
                "type": "string",
                "description": "Plaintext digest algorithm.",
            },
            "checksum_value": {
                "type": "string",
                "description": "SHA-256 hex of uncompressed source file.",
            },
            "compression": {"type": "string", "description": "identity or gzip."},
            "chunk_size": {"type": "integer", "description": "Read chunk size hint."},
            "offset": {
                "type": "integer",
                "description": "Starting offset (usually 0).",
            },
            "status": {"type": "string", "description": "Session status."},
            "completed": {"type": "boolean", "description": "Download finished flag."},
            "expires_at": {
                "type": ["string", "null"],
                "description": "Expiry ISO time.",
            },
            "plaintext_size_bytes": {
                "type": ["integer", "null"],
                "description": "Original file size when gzip alters wire size.",
            },
            "transport": {
                "type": "object",
                "description": "GET chunk URL template with offset and limit.",
                "properties": transport_props,
                "required": list(transport_props.keys()),
            },
        }
        return {
            "type": "object",
            "description": "Successful transfer_download_begin result.",
            "properties": {
                "success": {"type": "boolean", "const": True},
                "data": {
                    "type": "object",
                    "properties": data_props,
                    "required": ["transfer_id", "checksum_value", "transport"],
                },
            },
            "required": ["success", "data"],
        }


class TransferDownloadBeginCommand(Command):
    """
    Create a download session for a server-local source_path.

    Checksum fields refer to plaintext (pre-compression) content.
    """

    name = "transfer_download_begin"
    version = "1.0.0"
    descr = (
        "Registers a resumable download from a path readable by the server. Returns "
        "checksum metadata so clients can verify after streaming, and a GET chunk "
        "template with offset and limit query parameters matching "
        "GET /api/transfer/downloads/{transfer_id}/chunks. Authenticate the same way "
        "as other JSON-RPC commands; subsequent chunk requests need the same API key "
        "or Bearer header on the same host and scheme. Use identity to read raw bytes "
        "or gzip when the server serves a compressed wire representation."
    )
    category = "transfer"
    author = "Vasiliy Zdanovskiy"
    email = "vasilyvz@gmail.com"
    result_class = TransferDownloadBeginResult

    @classmethod
    def get_schema(cls) -> Dict[str, Any]:
        return {
            "type": "object",
            "description": (
                f"{cls.descr} source_path must be absolute and exist on the server. "
                "Invalid fields yield InvalidRequest."
            ),
            "properties": {
                "source_path": {
                    "type": "string",
                    "description": "Absolute path to the file on the server host.",
                },
                "filename": {
                    "type": "string",
                    "description": "Non-empty client-facing filename.",
                },
                "compression": {
                    "type": "string",
                    "enum": ["identity", "gzip"],
                    "description": "identity reads file as-is; gzip uses compressed wire form.",
                },
                "chunk_size": {
                    "type": "integer",
                    "description": "Optional read size; server clamps to max_chunk_size_bytes.",
                },
                "job_id": {
                    "type": "string",
                    "description": "Optional queue job id for WS correlation and artifact_ready.",
                },
                "correlation_id": {
                    "type": "string",
                    "description": "Optional opaque client correlation id.",
                },
                "command": {
                    "type": "string",
                    "description": "Optional command name stored on the session.",
                },
                "inbound_transfer_id": {
                    "type": "string",
                    "description": "Optional related upload transfer id.",
                },
                "delete_source_on_ack": {
                    "type": "boolean",
                    "description": "If true, transfer_ack may delete source under server policy.",
                },
            },
            "required": ["source_path", "filename", "compression"],
            "additionalProperties": False,
            "examples": [
                {
                    "command": "transfer_download_begin",
                    "params": {
                        "source_path": "/var/lib/app/export.bin",
                        "filename": "export.bin",
                        "compression": "identity",
                    },
                    "description": "Stream raw file bytes with identity compression.",
                },
                {
                    "command": "transfer_download_begin",
                    "params": {
                        "source_path": "/var/lib/app/archive.tgz",
                        "filename": "archive.tgz",
                        "compression": "gzip",
                    },
                    "description": "Gzip wire form: plaintext checksum still matches source file.",
                },
            ],
        }

    async def execute(self, **kwargs: Any) -> Any:
        """
        Args:
            **kwargs: source_path, filename, compression, optional chunk_size.

        Returns:
            Session receipt with transport hints or ErrorResult.
        """
        context = kwargs.pop("context", {})
        _ = context
        payload: dict[str, object] = {
            "source_path": kwargs["source_path"],
            "filename": kwargs["filename"],
            "compression": kwargs["compression"],
        }
        if "chunk_size" in kwargs:
            payload["chunk_size"] = kwargs["chunk_size"]
        for key in (
            "job_id",
            "correlation_id",
            "command",
            "inbound_transfer_id",
        ):
            if key in kwargs and kwargs[key] is not None:
                payload[key] = kwargs[key]
        if "delete_source_on_ack" in kwargs:
            payload["delete_source_on_ack"] = bool(kwargs["delete_source_on_ack"])
        try:
            data = await run_create_download_session(payload)
        except TransferPayloadValidationError as exc:
            return transfer_validation_error_result(exc.missing_fields)
        except (TransferTooLargeError, TransferCompressionError, TransferError) as exc:
            return transfer_domain_error_result(exc)
        tid = str(data["transfer_id"])
        transport = build_transfer_chunk_transport(
            direction="download", transfer_id=tid
        )
        merged: dict[str, object] = {**data, "transport": transport}
        return TransferDownloadBeginResult(merged)
