from __future__ import annotations

import argparse
from dataclasses import dataclass
from typing import Any, Callable, Dict, Optional, Sequence

from autotouch_cli.exceptions import AutotouchInputError

from autotouch_cli.templates import TemplateRuntime


@dataclass(frozen=True)
class CellCommandRuntime:
    resolve_token: Callable[[Optional[str], bool], Optional[str]]
    request_api: Callable[..., Any]
    print_json: Callable[[Any, bool], None]
    load_json_input: Callable[..., Any]
    emit_cells_schema: Callable[[argparse.Namespace, TemplateRuntime], None]
    template_runtime_factory: Callable[[], TemplateRuntime]


def cmd_cells_schema(args: argparse.Namespace, *, runtime: CellCommandRuntime) -> None:
    runtime.emit_cells_schema(args, runtime.template_runtime_factory())


def cmd_cells_patch(args: argparse.Namespace, *, runtime: CellCommandRuntime) -> None:
    token = runtime.resolve_token(args.token, required=True)
    payload = runtime.load_json_input(
        inline_json=args.updates_json,
        file_path=args.updates_file,
        context="updates",
        default=None,
    )
    if payload is None:
        raise AutotouchInputError("provide --updates-json or --updates-file")
    if isinstance(payload, list):
        payload = {"updates": payload}
    if not isinstance(payload, dict) or not isinstance(payload.get("updates"), list):
        raise AutotouchInputError("updates payload must be a list or an object with an 'updates' list")

    params = {"detect_types": "true"} if args.detect_types else None
    data = runtime.request_api(
        "PATCH",
        f"/api/tables/{args.table_id}/cells",
        base_url=args.base_url,
        token=token,
        use_x_api_key=args.use_x_api_key,
        params=params,
        payload=payload,
        timeout=args.timeout,
        verbose=args.verbose,
    )
    runtime.print_json(data, args.compact)


def cmd_cells_get(args: argparse.Namespace, *, runtime: CellCommandRuntime) -> None:
    token = runtime.resolve_token(args.token, required=True)
    data = runtime.request_api(
        "GET",
        f"/api/cells/{args.row_id}/{args.column_id}",
        base_url=args.base_url,
        token=token,
        use_x_api_key=args.use_x_api_key,
        timeout=args.timeout,
        verbose=args.verbose,
    )
    runtime.print_json(data, args.compact)


def register_cells_subcommands(
    subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
    *,
    add_api_common_arguments: Callable[[argparse.ArgumentParser], None],
    add_output_formatting_arguments: Callable[[argparse.ArgumentParser], None],
    handlers: Dict[str, Callable[[argparse.Namespace], None]],
    schema_types: Sequence[str],
) -> None:
    parser = subparsers.add_parser("cells", help="Cell operations")
    cells_sub = parser.add_subparsers(dest="cells_cmd", required=True)

    get_parser = cells_sub.add_parser("get", help="Get one cell with value/status/metadata")
    get_parser.add_argument("--row-id", required=True)
    get_parser.add_argument("--column-id", required=True)
    add_api_common_arguments(get_parser)
    get_parser.set_defaults(func=handlers["get"])

    patch_parser = cells_sub.add_parser(
        "patch",
        help="Patch cells in batch",
        description="Patch cells in batch. Use column key strings in updates[].key; this payload does not accept columnId.",
    )
    patch_parser.add_argument("--table-id", required=True)
    patch_parser.add_argument("--updates-json", help="JSON list or {updates:[...]} object")
    patch_parser.add_argument("--updates-file", help="Path to JSON file with updates payload")
    patch_parser.add_argument("--detect-types", action="store_true", help="Set detect_types=true query flag")
    add_api_common_arguments(patch_parser)
    patch_parser.set_defaults(func=handlers["patch"])

    schema_parser = cells_sub.add_parser("schema", help="Print cell payload schemas")
    schema_parser.add_argument("--type", choices=["all", *schema_types], default="all")
    schema_parser.add_argument("--out-file", help="Write schema JSON to file")
    add_output_formatting_arguments(schema_parser)
    schema_parser.set_defaults(func=handlers["schema"])
