"""
Blob command handlers for the remote CLI.

Handles dt blob cp, dt blob put, and dt blob get
with base64 encoding for file transfer and a 100MB size limit.
"""

from __future__ import annotations

import base64
import sys
from pathlib import Path
from typing import Any, Dict, List

import requests

from datatailr._remote_client.utils import Config, build_headers

BLOB_MAX_SIZE = 100 * 1024 * 1024  # 100 MB


def handle_blob_cp(
    cfg: Config,
    positionals: List[str],
    options: Dict[str, Any],
    send_help,
    handle_response,
    timeout_s: int = 60,
) -> int:
    if len(positionals) < 2:
        return send_help(cfg, "blob", "cp", timeout_s=timeout_s)

    source, destination = positionals[0], positionals[1]
    url = f"{cfg.base_url}/api/blob/cp"
    headers = build_headers(cfg)

    is_upload = not source.startswith("blob://")
    if is_upload:
        p = Path(source)
        if not p.exists() or not p.is_file():
            print(f"File not found: {source}", file=sys.stderr)
            return 2
        file_size = p.stat().st_size
        if file_size > BLOB_MAX_SIZE:
            print(
                f"File too large ({file_size} bytes). "
                f"Maximum allowed size is {BLOB_MAX_SIZE} bytes (100 MB).",
                file=sys.stderr,
            )
            return 2
        file_b64 = base64.b64encode(p.read_bytes()).decode("ascii")
        body: Dict[str, Any] = {
            "source_file_b64": file_b64,
            "source_file_name": p.name,
            "destination_bucket": destination,
        }
        body.update(options)
        try:
            resp = requests.post(url, headers=headers, json=body, timeout=timeout_s)
        except requests.RequestException:
            _print_unreachable(cfg)
            return 2
        return handle_response(resp, pretty_print=options.get("pretty", False))
    else:
        body = {
            "source_file": source,
            "destination_bucket": destination,
            "download": True,
        }
        body.update(options)
        try:
            resp = requests.post(url, headers=headers, json=body, timeout=timeout_s)
        except requests.RequestException:
            _print_unreachable(cfg)
            return 2

        if resp.status_code >= 400:
            return handle_response(resp)

        return _decode_b64_response(resp, _write_to=destination, blob_source=source)


def handle_blob_put(
    cfg: Config,
    positionals: List[str],
    options: Dict[str, Any],
    send_help,
    handle_response,
    timeout_s: int = 60,
) -> int:
    if len(positionals) < 1:
        return send_help(cfg, "blob", "put", timeout_s=timeout_s)

    destination = positionals[0]
    url = f"{cfg.base_url}/api/blob/put"
    headers = build_headers(cfg)

    content = sys.stdin.buffer.read()
    if len(content) > BLOB_MAX_SIZE:
        print(
            f"Input too large ({len(content)} bytes). "
            f"Maximum allowed size is {BLOB_MAX_SIZE} bytes (100 MB).",
            file=sys.stderr,
        )
        return 2

    body: Dict[str, Any] = {
        "destination_file": destination,
        "content_b64": base64.b64encode(content).decode("ascii"),
    }
    body.update(options)

    try:
        resp = requests.post(url, headers=headers, json=body, timeout=timeout_s)
    except requests.RequestException:
        _print_unreachable(cfg)
        return 2

    return handle_response(resp, pretty_print=options.get("pretty", False))


def handle_blob_get(
    cfg: Config,
    positionals: List[str],
    options: Dict[str, Any],
    send_help,
    handle_response,
    timeout_s: int = 60,
) -> int:
    if len(positionals) < 1:
        return send_help(cfg, "blob", "get", timeout_s=timeout_s)

    url = f"{cfg.base_url}/api/blob/get"
    headers = build_headers(cfg)
    query: Dict[str, Any] = {"bucket_name": positionals[0]}
    if len(positionals) > 1:
        query["file_name"] = positionals[1]

    try:
        resp = requests.get(url, headers=headers, params=query, timeout=timeout_s)
    except requests.RequestException:
        _print_unreachable(cfg)
        return 2

    if resp.status_code >= 400:
        return handle_response(resp)

    return _decode_b64_response(resp, _write_to=None)


# ── Private helpers ──────────────────────────────────────────────────


def _print_unreachable(cfg: Config) -> None:
    print(
        f"Cannot reach the platform at {cfg.base_url}. Is the platform running?\n"
        "If the URL is wrong, run 'datatailr login' to update it.",
        file=sys.stderr,
    )


def _decode_b64_response(
    resp: requests.Response,
    _write_to: str | None,
    blob_source: str | None = None,
) -> int:
    try:
        data = resp.json()
    except Exception:
        # Not valid JSON — treat raw body as the content
        data = resp.text

    # Plain text response: data is the base64 content directly
    if isinstance(data, str):
        content_b64 = data
    else:
        err = data.get("error", "")
        if err:
            print(err, file=sys.stderr)
            return 1
        content_b64 = data.get("result", "")

    if not content_b64:
        print(
            "No content received from server. Perhaps the blob is empty?",
            file=sys.stderr,
        )
        return 1

    file_bytes = base64.b64decode(content_b64)

    if _write_to is not None:
        dest_path = Path(_write_to)
        if dest_path.is_dir() and blob_source:
            blob_name = (
                blob_source.split("/")[-1] if "/" in blob_source else "downloaded_file"
            )
            dest_path = dest_path / blob_name
        dest_path.write_bytes(file_bytes)
    else:
        sys.stdout.buffer.write(file_bytes)

    return 0
