"""Service helpers for the unified 4-command UX (`make/protect/check/launch`)."""

from __future__ import annotations

import argparse
import json
import subprocess
import sys
from pathlib import Path
from typing import Any, Callable, Dict, Optional, Sequence, Tuple


def build_pipeline_args_from_make(args: argparse.Namespace) -> argparse.Namespace:
    """Translate `make` args to internal `pipeline` args."""
    make_mode = str(args.mode)
    pipeline_mode = "merge" if make_mode == "cross-merge" else make_mode
    force_cross = make_mode == "cross-merge"
    return argparse.Namespace(
        command="pipeline",
        mode=pipeline_mode,
        output_type=args.output_type,
        models=list(args.models) if args.models else None,
        source=args.source,
        target=args.target,
        output=args.output,
        name=args.name,
        compression_profile="lossless-max",
        compression_engine="auto",
        sample_data=args.sample_data,
        auto_grow=args.auto_grow,
        merge_strategy=args.merge_strategy,
        strategy_engine=args.strategy_engine,
        cross_opt_stage=args.cross_opt_stage,
        weights=args.weights,
        coverage_floor=float(args.coverage_floor),
        top_k=int(args.top_k),
        train_gate=args.train_gate,
        hf_base_model=args.hf_base_model,
        onnx_input_shape=args.onnx_input_shape,
        trace_input_shape=args.trace_input_shape,
        epochs=int(args.epochs),
        seed=int(args.seed),
        allow_partial_load=bool(args.allow_partial_load),
        allow_lossy_cast=bool(args.allow_lossy_cast),
        trust_sign=True,
        provenance="private",
        repro_mode="strict",
        signer_id="default",
        signer_backend="local-keyring",
        dry_run=bool(args.dry_run),
        report_file=args.report_file,
        self_check=bool(args.self_check),
        self_check_level=args.self_check_level,
        force_cross=force_cross,
    )


def build_build_args_from_protect(args: argparse.Namespace) -> argparse.Namespace:
    """Translate `protect` args to internal `build` args."""
    return argparse.Namespace(
        command="build",
        model=args.model,
        output=args.output,
        name=args.name,
        compression_profile=args.compression_profile,
        compression_engine="auto",
        trust_sign=args.trust_sign,
        provenance=args.provenance,
        repro_mode=args.repro_mode,
        signer_id=args.signer_id,
        signer_backend=args.signer_backend,
    )


def run_cli_json_subcommand(argv: Sequence[str], *, timeout_seconds: int = 300) -> Dict[str, Any]:
    """Run a CLI subcommand and parse the first JSON object from output."""
    cmd = [sys.executable, "-m", "model_osmosis.cli", *list(argv)]
    proc = subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        timeout=timeout_seconds,
    )
    output = (proc.stdout or "") + ("\n" + proc.stderr if proc.stderr else "")
    if proc.returncode != 0:
        raise RuntimeError(output.strip() or f"subcommand failed: {' '.join(argv)}")
    idx = output.find("{")
    if idx < 0:
        raise RuntimeError(f"subcommand returned no JSON payload: {' '.join(argv)}")
    return json.loads(output[idx:])


def evaluate_check_workflow(
    *,
    image: str,
    self_check_level: str,
    trust_profile: str,
    require_trust: bool,
    policy_file: Optional[str],
    benchmark_runtime: bool,
    benchmark_profile: str,
    runtime_profile: str,
    device: str,
    runtime_quantization: str,
    run_release_check: bool,
    trust_checker: Callable[..., Dict[str, Any]],
    self_checker: Callable[..., Dict[str, Any]],
    json_subcommand_runner: Callable[[Sequence[str]], Dict[str, Any]] = run_cli_json_subcommand,
) -> Dict[str, Any]:
    """Execute the unified `check` workflow and return deterministic payload."""
    from model_osmosis.format.image import OsmoImage

    inspect_info = OsmoImage.inspect(image)
    trust_info = trust_checker(
        image,
        action="check",
        require_trust=require_trust,
        policy_file=policy_file,
        trust_profile=trust_profile,
    )
    self_check = self_checker(
        output_type="osmoimg",
        output_path=Path(image),
        self_check_level=self_check_level,
        reference_model=None,
    )

    optional_checks: Dict[str, Any] = {}
    failures = []

    if benchmark_runtime:
        try:
            bench_payload = json_subcommand_runner(
                [
                    "benchmark-runtime",
                    str(image),
                    "--profile",
                    str(benchmark_profile),
                    "--runtime-profile",
                    str(runtime_profile),
                    "--device",
                    str(device),
                    "--runtime-quantization",
                    str(runtime_quantization),
                    "--json",
                ]
            )
            optional_checks["benchmark_runtime"] = {
                "ran": True,
                "passed": bool(bench_payload.get("passed", False)),
                "profile": str(bench_payload.get("profile", benchmark_profile)),
            }
            if not optional_checks["benchmark_runtime"]["passed"]:
                failures.append("benchmark_runtime")
        except Exception as exc:
            optional_checks["benchmark_runtime"] = {
                "ran": True,
                "passed": False,
                "reason": str(exc),
            }
            failures.append("benchmark_runtime")

    if run_release_check:
        try:
            release_payload = json_subcommand_runner(["release-check", "--json"])
            optional_checks["release_check"] = {
                "ran": True,
                "passed": str(release_payload.get("status", "")).upper() == "PASS",
                "failed_gates": list(release_payload.get("failed_gates", [])),
            }
            if not optional_checks["release_check"]["passed"]:
                failures.append("release_check")
        except Exception as exc:
            optional_checks["release_check"] = {
                "ran": True,
                "passed": False,
                "reason": str(exc),
            }
            failures.append("release_check")

    return {
        "status": "PASS" if not failures else "FAIL",
        "image": str(image),
        "trust_profile": trust_profile,
        "require_trust": bool(require_trust),
        "trust_status": str(trust_info.get("trust_status", "unsigned")),
        "payload_codec": str(inspect_info.get("runtime", {}).get("payload_codec", "unknown")),
        "compression_profile": str(inspect_info.get("runtime", {}).get("compression_profile", "legacy")),
        "self_check_status": str(self_check.get("self_check_status", "unknown")),
        "self_check_level": str(self_check.get("self_check_level", self_check_level)),
        "self_check_checks": list(self_check.get("self_check_checks", [])),
        "self_check_skips": list(self_check.get("self_check_skips", [])),
        "optional_checks": optional_checks,
        "failed_checks": sorted(set(failures)),
    }


def build_launch_action_namespace(args: argparse.Namespace) -> Tuple[str, argparse.Namespace]:
    """Translate `launch` args into target command namespace for run/serve/export."""
    action = str(args.action).strip().lower()
    if action == "run":
        return (
            "run",
            argparse.Namespace(
                command="run",
                image=args.image,
                prompt=args.prompt,
                input=args.input,
                output_file=args.output_file,
                max_tokens=args.max_tokens,
                temperature=args.temperature,
                include_prompt=args.include_prompt,
                runtime_profile=args.runtime_profile,
                device=args.device,
                runtime_quantization=args.runtime_quantization,
                runtime_cache=args.runtime_cache,
                require_trust=args.require_trust,
                trust_profile=args.trust_profile,
                policy_file=args.policy_file,
            ),
        )
    if action == "serve":
        return (
            "serve",
            argparse.Namespace(
                command="serve",
                image=args.image,
                host=args.host,
                port=int(args.port),
                runtime_profile=args.runtime_profile,
                device=args.device,
                runtime_quantization=args.runtime_quantization,
                runtime_cache=args.runtime_cache,
                warmup=args.warmup,
                require_trust=args.require_trust,
                trust_profile=args.trust_profile,
                policy_file=args.policy_file,
            ),
        )
    if action == "export":
        return (
            "export",
            argparse.Namespace(
                command="export",
                image=args.image,
                format=args.format,
                output=args.output,
                require_trust=args.require_trust,
                trust_profile=args.trust_profile,
                policy_file=args.policy_file,
            ),
        )
    raise RuntimeError(
        f"Unsupported launch action '{action}'. "
        "Expected one of: run, serve, export."
    )

