# Copyright (c) Metis. All rights reserved.

"""Mantis platform CLI — register agents and manage algorithms.

Entry point: `mantis` (registered in pyproject.toml).

Usage::

    mantis deploy <image> [--manifest-dir ./mantis_manifest] [--skip-build]
    mantis algorithm list
    mantis algorithm new <name>
"""

from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Optional

try:
    import typer
except ImportError:
    print(
        "typer is required for the mantis CLI. Install with: pip install typer",
        file=sys.stderr,
    )
    sys.exit(1)

try:
    import httpx
except ImportError:
    print(
        "httpx is required for the mantis CLI. Install with: pip install httpx",
        file=sys.stderr,
    )
    sys.exit(1)

app = typer.Typer(
    name="mantis",
    help="Mantis platform CLI — register agents and manage algorithms.",
    no_args_is_help=True,
)

# =============================================================================
# deploy command
# =============================================================================


@app.command()
def deploy(
    image: Optional[str] = typer.Argument(
        None,
        help="Docker image to register. If omitted, builds from project dir.",
    ),
    project_dir: str = typer.Option(
        ".",
        "--project-dir",
        help="Agent project directory (for building).",
    ),
    manifest_dir: str = typer.Option(
        "./mantis_manifest",
        help="Path to the mantis_manifest/ directory.",
    ),
    skip_build: bool = typer.Option(
        False,
        "--skip-build",
        help="Skip Docker build, register image only.",
    ),
    dockerfile: Optional[str] = typer.Option(
        None,
        "--dockerfile",
        help="Use specific Dockerfile (e.g., Dockerfile.gpu).",
    ),
    tag: Optional[str] = typer.Option(
        None,
        "--tag",
        help="Override image tag.",
    ),
    dry_run: bool = typer.Option(
        False,
        "--dry-run",
        help="Validate manifest only, don't build or register.",
    ),
    host: Optional[str] = typer.Option(
        None,
        envvar="INSIGHT_HOST",
        help="Insight server URL (e.g., http://localhost:3000).",
    ),
    api_key: Optional[str] = typer.Option(
        None,
        envvar="INSIGHT_SECRET_KEY",
        help="Insight project API key (sk-lf-...).",
    ),
) -> None:
    """Build, push, and register an agent with the Mantis platform.

    Examples::

        # Full pipeline: build + push + register
        mantis deploy --tag myagent:v1

        # Register pre-built image (skip build)
        mantis deploy myregistry/myagent:v1 --skip-build

        # Use specific Dockerfile
        mantis deploy --tag myagent:v1 --dockerfile Dockerfile.gpu

        # Validate manifest only
        mantis deploy --dry-run
    """
    from mantisdk.platform.manifest import parse_manifest, parse_secrets, parse_task_schema

    manifest_path = Path(manifest_dir)
    if not manifest_path.exists():
        typer.echo(f"Error: Manifest directory not found: {manifest_path}", err=True)
        raise typer.Exit(1)

    # Parse manifest
    try:
        manifest = parse_manifest(manifest_path)
    except (FileNotFoundError, ValueError) as e:
        typer.echo(f"Error parsing manifest: {e}", err=True)
        raise typer.Exit(1)

    # Parse optional task schema
    task_schema = parse_task_schema(manifest_path)

    typer.echo(f"Validating manifest...")
    typer.echo(f"  Name: {manifest.name}")
    typer.echo(f"  Version: {manifest.version}")
    typer.echo(f"  Capabilities: {', '.join(manifest.capabilities) or 'none'}")
    if task_schema:
        typer.echo(f"  Task schema: found")

    if dry_run:
        typer.echo("Dry-run complete. Manifest is valid.")
        return

    # Determine image tag
    image_tag = image or tag
    if not image_tag and not skip_build:
        image_tag = f"{manifest.name}:latest"

    # Build if needed
    if not skip_build and not image:
        from mantisdk.platform.builder import build_image, detect_build_strategy

        proj = Path(project_dir)
        strategy = detect_build_strategy(proj, dockerfile)
        typer.echo(f"\nBuilding image '{image_tag}' (strategy: {strategy.value})...")

        try:
            image_tag = build_image(proj, image_tag, dockerfile=dockerfile)
            typer.echo(f"  Built successfully!")
        except (RuntimeError, FileNotFoundError) as e:
            typer.echo(f"Build failed: {e}", err=True)
            raise typer.Exit(1)

        # Extract image digest for pinning
        from mantisdk.platform.builder import get_image_digest

        image_digest = get_image_digest(image_tag)

    if not image_tag:
        typer.echo("Error: No image specified. Provide an image argument or use --tag.", err=True)
        raise typer.Exit(1)

    # Resolve credentials (from flags, env, or config)
    from mantisdk.cli.http_client import get_credentials

    resolved_host, resolved_public, resolved_secret = get_credentials(host, api_key)

    typer.echo(f"\nRegistering agent '{manifest.name}' with image '{image_tag}'...")

    # Build request payload (includes runner/resource config from agent.yml)
    from dataclasses import asdict

    # image_digest is set during build; not available for --skip-build
    _digest = locals().get("image_digest")

    payload: dict = {
        "name": manifest.name,
        "image": image_tag,
        "manifest": {
            "version": manifest.version,
            "capabilities": manifest.capabilities,
            "resources": manifest.resources,
            "description": manifest.description,
            "runner": asdict(manifest.runner),
            "resource_limits": asdict(manifest.resource_limits),
            "env": manifest.env,
        },
        "capabilities": manifest.capabilities,
        "resources": {
            **manifest.resources,
            "runner": asdict(manifest.runner),
            "resource_limits": asdict(manifest.resource_limits),
        },
        "description": manifest.description,
    }

    if _digest is not None:
        payload["imageDigest"] = _digest

    if task_schema is not None:
        payload["taskInputSchema"] = task_schema.input_schema

    # POST to Insight
    url = f"{resolved_host.rstrip('/')}/api/public/agents/register"
    try:
        response = httpx.post(
            url,
            json=payload,
            auth=(resolved_public, resolved_secret),
            timeout=30.0,
        )
    except httpx.RequestError as e:
        typer.echo(f"Error connecting to {resolved_host}: {e}", err=True)
        raise typer.Exit(1)

    if response.status_code == 201:
        data = response.json()
        typer.echo(f"Agent registered successfully!")
        typer.echo(f"  ID: {data.get('id')}")
        typer.echo(f"  Name: {data.get('name')}")
        typer.echo(f"  Compatible algorithms: {', '.join(data.get('compatibleAlgorithms', []))}")
    else:
        typer.echo(f"Registration failed (HTTP {response.status_code}):", err=True)
        try:
            typer.echo(f"  {json.dumps(response.json(), indent=2)}", err=True)
        except Exception:
            typer.echo(f"  {response.text}", err=True)
        raise typer.Exit(1)

    # Upload secrets from mantis_manifest/.env (if present)
    secrets = parse_secrets(manifest_path)
    if secrets:
        typer.echo(f"\nUploading {len(secrets)} secret(s)...")
        secrets_client = httpx.Client(
            base_url=resolved_host.rstrip("/"),
            auth=(resolved_public, resolved_secret),
            timeout=15.0,
        )
        for secret_name, secret_value in secrets.items():
            try:
                # Try create first
                resp = secrets_client.post(
                    "/api/public/secrets",
                    json={"name": secret_name, "value": secret_value, "scope": "agent"},
                )
                if resp.status_code in (200, 201):
                    typer.echo(f"  {secret_name} (created)")
                elif resp.status_code == 409:
                    # Already exists — update
                    resp = secrets_client.put(
                        f"/api/public/secrets/{secret_name}",
                        json={"value": secret_value, "scope": "agent"},
                    )
                    if resp.status_code in (200, 201):
                        typer.echo(f"  {secret_name} (updated)")
                    else:
                        typer.echo(f"  {secret_name} (update failed: HTTP {resp.status_code})", err=True)
                else:
                    typer.echo(f"  {secret_name} (failed: HTTP {resp.status_code})", err=True)
            except Exception as e:
                typer.echo(f"  {secret_name} (error: {e})", err=True)
        secrets_client.close()


# =============================================================================
# init command — scaffold agent project
# =============================================================================

_DOCKERFILE_TEMPLATE = """\
# Auto-generated by `mantis init`
FROM python:3.12-slim

RUN apt-get update && apt-get install -y --no-install-recommends \\
    build-essential \\
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Install mantisdk with agent extras (includes mantis-agent CLI)
RUN pip install --no-cache-dir "mantisdk[agent]"

# Copy agent code
COPY . /app/agent/

ENV PYTHONPATH="/app:/app/agent"

# mantis-agent handles: store client, tracer, runner init, rollout polling
CMD ["mantis-agent", "--agent", "{agent_ref}"]
"""

_AGENT_YML_TEMPLATE = """\
# Agent manifest for Mantis Platform
# Generated by `mantis init`

name: {name}
version: "1.0.0"
description: >
  {description}

capabilities:
  - prompt-optimization
  - tool-use

runner:
  n_runners: 1
  poll_interval: 5.0

resources:
  memory: "4g"
  cpu: 1.0
  gpu: false

env: {{}}
"""


@app.command()
def init(
    agent_ref: str = typer.Option(
        ...,
        "--agent",
        "-a",
        help="Agent reference in module:attribute format (e.g., crm_agent:crm_agent).",
    ),
    name: Optional[str] = typer.Option(
        None,
        "--name",
        "-n",
        help="Agent name. Defaults to module name.",
    ),
    output_dir: str = typer.Option(
        ".",
        "--output-dir",
        "-o",
        help="Output directory for generated files.",
    ),
) -> None:
    """Scaffold a Mantis agent project with Dockerfile and manifest.

    Generates:
    - Dockerfile with mantis-agent entrypoint
    - mantis_manifest/agent.yml with default configuration

    Examples::

        mantis init --agent crm_agent:crm_agent
        mantis init --agent my_agent:agent --name my-cool-agent
    """
    output = Path(output_dir)

    # Derive name from module if not provided
    module_name = agent_ref.split(":")[0]
    agent_name = name or module_name.replace("_", "-")
    description = f"Mantis agent: {agent_name}"

    # Generate Dockerfile
    dockerfile_path = output / "Dockerfile"
    if dockerfile_path.exists():
        typer.echo(f"Warning: {dockerfile_path} already exists, skipping.")
    else:
        dockerfile_content = _DOCKERFILE_TEMPLATE.format(agent_ref=agent_ref)
        dockerfile_path.write_text(dockerfile_content)
        typer.echo(f"Created {dockerfile_path}")

    # Generate manifest
    manifest_dir = output / "mantis_manifest"
    manifest_dir.mkdir(parents=True, exist_ok=True)
    agent_yml_path = manifest_dir / "agent.yml"
    if agent_yml_path.exists():
        typer.echo(f"Warning: {agent_yml_path} already exists, skipping.")
    else:
        agent_yml_content = _AGENT_YML_TEMPLATE.format(
            name=agent_name,
            description=description,
        )
        agent_yml_path.write_text(agent_yml_content)
        typer.echo(f"Created {agent_yml_path}")

    # Generate .gitignore (includes mantis_manifest/.env)
    gitignore_path = output / ".gitignore"
    gitignore_entry = "mantis_manifest/.env"
    if gitignore_path.exists():
        existing = gitignore_path.read_text()
        if gitignore_entry not in existing:
            with open(gitignore_path, "a") as f:
                f.write(f"\n# Mantis secrets (API keys)\n{gitignore_entry}\n")
            typer.echo(f"Updated {gitignore_path} (added {gitignore_entry})")
    else:
        gitignore_path.write_text(f"# Mantis secrets (API keys — never commit)\n{gitignore_entry}\n")
        typer.echo(f"Created {gitignore_path}")

    # Generate sample .env in mantis_manifest/
    env_path = manifest_dir / ".env"
    if not env_path.exists():
        env_path.write_text(
            "# API keys for the agent container\n"
            "# These are uploaded to the platform as encrypted secrets during `mantis deploy`\n"
            "# ANTHROPIC_API_KEY=sk-ant-...\n"
            "# OPENAI_API_KEY=sk-...\n"
        )
        typer.echo(f"Created {env_path} (sample — add your API keys)")

    typer.echo(f"\nAgent project scaffolded!")
    typer.echo(f"  Agent ref: {agent_ref}")
    typer.echo(f"  Name: {agent_name}")
    typer.echo(f"\nNext steps:")
    typer.echo(f"  1. Add your agent code and requirements.txt")
    typer.echo(f"  2. Add API keys to mantis_manifest/.env")
    typer.echo(f"  3. Build & deploy: mantis deploy --tag {agent_name}:latest")


# =============================================================================
# algorithm subcommands
# =============================================================================

algorithm_app = typer.Typer(
    name="algorithm",
    help="List and manage algorithms.",
    no_args_is_help=True,
)
app.add_typer(algorithm_app, name="algorithm")


@algorithm_app.command("list")
def algorithm_list() -> None:
    """List all available algorithms from class metadata."""
    from mantisdk.algorithm.base import Algorithm

    subclasses = Algorithm.__subclasses__()
    if not subclasses:
        typer.echo("No algorithms found. Import algorithm modules first.")
        return

    typer.echo(f"{'Name':<12} {'Display':<16} {'GPU':<6} {'Description'}")
    typer.echo("-" * 70)
    for cls in subclasses:
        gpu = cls.requirements.get("gpu", False) if cls.requirements else False
        typer.echo(f"{cls.name:<12} {cls.display_name:<16} {str(gpu):<6} {cls.description}")


@algorithm_app.command("new")
def algorithm_new(
    name: str = typer.Argument(..., help="Algorithm name (e.g., my_algo)"),
    output_dir: str = typer.Option(".", help="Directory to create the algorithm in"),
) -> None:
    """Scaffold a new algorithm class."""
    output_path = Path(output_dir) / f"{name}.py"
    if output_path.exists():
        typer.echo(f"Error: {output_path} already exists.", err=True)
        raise typer.Exit(1)

    template = f'''"""Custom algorithm: {name}."""

from __future__ import annotations

from typing import Any, ClassVar, Dict, Optional

from mantisdk.algorithm.base import Algorithm
from mantisdk.types import Dataset


class {name.title().replace("_", "")}(Algorithm):
    """TODO: Describe your algorithm."""

    name: ClassVar[str] = "{name}"
    display_name: ClassVar[str] = "{name.title().replace("_", " ")}"
    description: ClassVar[str] = "TODO: Add description"
    requirements: ClassVar[Dict[str, Any]] = {{"gpu": False, "min_runners": 1}}

    def run(
        self,
        train_dataset: Optional[Dataset[Any]] = None,
        val_dataset: Optional[Dataset[Any]] = None,
    ) -> None:
        trainer = self.get_trainer()
        store = self.get_store()

        # TODO: Implement your algorithm logic here
        raise NotImplementedError("Implement run() for {name}")
'''

    output_path.write_text(template)
    typer.echo(f"Created algorithm scaffold: {output_path}")


# =============================================================================
# auth commands
# =============================================================================

auth_app = typer.Typer(
    name="auth",
    help="Manage authentication credentials.",
    no_args_is_help=True,
)
app.add_typer(auth_app, name="auth")


@auth_app.command("login")
def auth_login(
    host: str = typer.Option(..., prompt=True, help="Insight server URL"),
    public_key: str = typer.Option(..., prompt=True, help="Project public key (pk-lf-...)"),
    api_key: str = typer.Option(..., prompt=True, hide_input=True, help="Project secret key (sk-lf-...)"),
) -> None:
    """Store credentials for the mantis CLI."""
    from mantisdk.cli.http_client import save_config

    save_config({"host": host.rstrip("/"), "public_key": public_key, "api_key": api_key})
    typer.echo("Credentials saved to ~/.mantis/config.json")


@auth_app.command("logout")
def auth_logout() -> None:
    """Clear stored credentials."""
    from mantisdk.cli.http_client import CONFIG_FILE

    if CONFIG_FILE.exists():
        CONFIG_FILE.unlink()
        typer.echo("Credentials cleared.")
    else:
        typer.echo("No credentials stored.")


@auth_app.command("status")
def auth_status() -> None:
    """Show current authentication status."""
    from mantisdk.cli.http_client import get_config

    config = get_config()
    host = config.get("host")
    has_key = bool(config.get("api_key"))

    if host:
        typer.echo(f"Host: {host}")
        typer.echo(f"API key: {'configured' if has_key else 'not set'}")
    else:
        typer.echo("No credentials stored. Run `mantis auth login`.")


# =============================================================================
# secret commands
# =============================================================================

secret_app = typer.Typer(
    name="secret",
    help="Manage project secrets (encrypted env vars for agent containers).",
    no_args_is_help=True,
)
app.add_typer(secret_app, name="secret")


@secret_app.command("set")
def secret_set(
    name: str = typer.Argument(..., help="Secret name (UPPER_SNAKE_CASE, e.g. TAVILY_API_KEY)"),
    value: Optional[str] = typer.Option(
        None,
        "--value",
        "-v",
        help="Secret value. If omitted, you will be prompted.",
    ),
    description: Optional[str] = typer.Option(None, "--description", "-d", help="Human-readable description"),
    scope: str = typer.Option("agent", help="Scope: agent, worker, or both"),
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """Create or update a project secret."""
    from mantisdk.cli.http_client import create_client

    if value is None:
        value = typer.prompt("Secret value", hide_input=True)

    client = create_client(host, api_key)
    payload: dict = {"name": name, "value": value, "scope": scope}
    if description:
        payload["description"] = description

    response = client.post("/api/public/secrets", json=payload)

    if response.status_code in (200, 201):
        data = response.json()
        typer.echo(f"Secret '{data.get('name', name)}' saved ({data.get('displayValue', '****')})")
    elif response.status_code == 409:
        # Already exists -- update
        response = client.put(f"/api/public/secrets/{name}", json=payload)
        if response.status_code in (200, 201):
            typer.echo(f"Secret '{name}' updated.")
        else:
            typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
            raise typer.Exit(1)
    else:
        typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
        raise typer.Exit(1)


@secret_app.command("list")
def secret_list(
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """List project secrets (names and descriptions only, not values)."""
    from mantisdk.cli.http_client import create_client

    client = create_client(host, api_key)
    response = client.get("/api/public/secrets")

    if response.status_code == 200:
        secrets = response.json().get("secrets", [])
        if not secrets:
            typer.echo("No secrets configured. Run `mantis secret set <name>` to add one.")
            return
        typer.echo(f"{'Name':<30} {'Scope':<10} {'Description'}")
        typer.echo("-" * 70)
        for s in secrets:
            desc = (s.get("description") or "")[:28]
            typer.echo(f"{s.get('name', ''):<30} {s.get('scope', ''):<10} {desc}")
    else:
        typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
        raise typer.Exit(1)


@secret_app.command("delete")
def secret_delete(
    name: str = typer.Argument(..., help="Secret name to delete"),
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """Delete a project secret."""
    from mantisdk.cli.http_client import create_client

    client = create_client(host, api_key)
    response = client.delete(f"/api/public/secrets/{name}")

    if response.status_code in (200, 204):
        typer.echo(f"Secret '{name}' deleted.")
    else:
        typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
        raise typer.Exit(1)


# =============================================================================
# agent commands
# =============================================================================

agent_app = typer.Typer(
    name="agent",
    help="Manage registered agents.",
    no_args_is_help=True,
)
app.add_typer(agent_app, name="agent")


@agent_app.command("list")
def agent_list(
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """List registered agents."""
    from mantisdk.cli.http_client import create_client

    client = create_client(host, api_key)
    response = client.get("/api/public/agents")

    if response.status_code == 200:
        data = response.json()
        agents = data.get("agents", [])
        if not agents:
            typer.echo("No agents registered. Run `mantis deploy` to register an agent.")
            return
        typer.echo(f"{'Name':<20} {'Image':<30} {'Compatible'}")
        typer.echo("-" * 70)
        for a in agents:
            compat = ", ".join(a.get("compatibleAlgorithms", []))
            typer.echo(f"{a.get('name', ''):<20} {a.get('image', '')[:28]:<30} {compat}")
    else:
        typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
        raise typer.Exit(1)


# =============================================================================
# run commands
# =============================================================================

run_app = typer.Typer(
    name="run",
    help="Manage platform optimization runs.",
    no_args_is_help=True,
)
app.add_typer(run_app, name="run")


@run_app.command("start")
def run_start(
    algorithm: str = typer.Argument(..., help="Algorithm name (e.g., gepa)"),
    agent_id: str = typer.Option(..., "--agent", help="Registered agent ID"),
    dataset_id: str = typer.Option(..., "--dataset", help="Dataset ID"),
    name: Optional[str] = typer.Option(None, "--name", help="Run name"),
    config_json: Optional[str] = typer.Option(None, "--config", help="JSON config string"),
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """Start a platform optimization run."""
    from mantisdk.cli.http_client import create_client

    client = create_client(host, api_key)

    payload: dict = {
        "algorithmName": algorithm,
        "agentId": agent_id,
        "datasetId": dataset_id,
    }
    if name:
        payload["name"] = name
    if config_json:
        payload["config"] = json.loads(config_json)

    response = client.post("/api/public/runs/start", json=payload)

    if response.status_code in (200, 201):
        data = response.json()
        typer.echo(f"Run started!")
        typer.echo(f"  Run ID: {data.get('runId')}")
        typer.echo(f"  Run #:  {data.get('runNumber')}")
        typer.echo(f"  Job ID: {data.get('jobId')}")
        typer.echo(f"  Status: {data.get('status')}")
    else:
        typer.echo(f"Failed (HTTP {response.status_code}):", err=True)
        typer.echo(f"  {response.text}", err=True)
        raise typer.Exit(1)


@run_app.command("verify")
def run_verify(
    algorithm: str = typer.Argument(..., help="Algorithm name"),
    agent_id: str = typer.Option(..., "--agent", help="Agent ID"),
    dataset_id: Optional[str] = typer.Option(None, "--dataset", help="Dataset ID"),
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """Verify an algorithm configuration."""
    from mantisdk.cli.http_client import create_client

    client = create_client(host, api_key)
    payload: dict = {"algorithmName": algorithm, "agentId": agent_id}
    if dataset_id:
        payload["datasetId"] = dataset_id

    response = client.post("/api/public/runs/verify", json=payload)

    if response.status_code in (200, 202):
        data = response.json()
        typer.echo(f"Verify job queued: {data.get('jobId')}")
    else:
        typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
        raise typer.Exit(1)


@run_app.command("list")
def run_list(
    source: Optional[str] = typer.Option(None, help="Filter by source (platform/sdk)"),
    limit: int = typer.Option(20, help="Max results"),
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """List runs."""
    from mantisdk.cli.http_client import create_client

    client = create_client(host, api_key)
    params: dict = {"limit": str(limit)}
    if source:
        params["source"] = source

    response = client.get("/api/public/runs", params=params)

    if response.status_code == 200:
        data = response.json()
        runs = data.get("runs", [])
        if not runs:
            typer.echo("No runs found.")
            return
        typer.echo(f"{'#':<6} {'Name':<30} {'State':<12} {'Source':<10}")
        typer.echo("-" * 60)
        for r in runs:
            typer.echo(
                f"{r.get('runNumber', ''):<6} "
                f"{(r.get('name') or '')[:28]:<30} "
                f"{r.get('state', ''):<12} "
                f"{r.get('source', ''):<10}"
            )
    else:
        typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
        raise typer.Exit(1)


# =============================================================================
# dataset commands
# =============================================================================

dataset_app = typer.Typer(
    name="dataset",
    help="Manage datasets.",
    no_args_is_help=True,
)
app.add_typer(dataset_app, name="dataset")


@dataset_app.command("upload")
def dataset_upload(
    path: str = typer.Argument(..., help="Path to JSON or JSONL file"),
    name: str = typer.Option(..., "--name", help="Dataset name"),
    agent: Optional[str] = typer.Option(
        None,
        "--agent",
        help="Validate items against this agent's task schema",
    ),
    description: Optional[str] = typer.Option(None, help="Description"),
    host: Optional[str] = typer.Option(None, envvar="INSIGHT_HOST"),
    api_key: Optional[str] = typer.Option(None, envvar="INSIGHT_SECRET_KEY"),
) -> None:
    """Upload a JSON or JSONL file as a dataset.

    Supports:
      - JSON with {"tasks": [...]} or {"items": [...]} wrapper
      - JSON bare array [item1, item2, ...]
      - JSONL (one JSON object per line)

    Examples::

        mantis dataset upload tasks.json --name crm-benchmark-v1
        mantis dataset upload tasks.json --name crm-v1 --agent crm-analyst
        mantis dataset upload tasks.jsonl --name eval-set
    """
    from mantisdk.cli.http_client import create_client

    file_path = Path(path)
    if not file_path.exists():
        typer.echo(f"Error: File not found: {path}", err=True)
        raise typer.Exit(1)

    # Read items -- auto-detect JSON vs JSONL
    with open(file_path) as f:
        raw = f.read().strip()

    if file_path.suffix == ".json" or raw.startswith("{") or raw.startswith("["):
        data = json.loads(raw)
        if isinstance(data, list):
            items = data
        elif isinstance(data, dict):
            items = data.get("tasks") or data.get("items") or []
        else:
            items = []
    else:
        # JSONL: one item per line
        items = [json.loads(line) for line in raw.splitlines() if line.strip()]

    if not items:
        typer.echo("Error: No items found in file.", err=True)
        raise typer.Exit(1)

    typer.echo(f"Found {len(items)} items in {file_path.name}")

    # Schema validation against agent's taskInputSchema
    if agent:
        client = create_client(host, api_key)
        resp = client.get("/api/public/agents")
        if resp.status_code != 200:
            typer.echo(f"Warning: Could not fetch agents for validation: HTTP {resp.status_code}", err=True)
        else:
            agents_list = resp.json().get("agents", [])
            matched = [a for a in agents_list if a.get("name") == agent]
            if not matched:
                typer.echo(f"Warning: Agent '{agent}' not found, skipping validation", err=True)
            else:
                schema = matched[0].get("taskInputSchema")
                if schema:
                    try:
                        import jsonschema
                    except ImportError:
                        typer.echo(
                            "Warning: jsonschema not installed, skipping validation. "
                            "Install with: pip install jsonschema",
                            err=True,
                        )
                        schema = None

                    if schema:
                        typer.echo(f"Validating against agent '{agent}' schema...")
                        errors = []
                        for i, item in enumerate(items):
                            try:
                                jsonschema.validate(item, schema)
                            except jsonschema.ValidationError as e:
                                item_name = item.get("name", f"item {i}") if isinstance(item, dict) else f"item {i}"
                                errors.append(f"  [{i+1}/{len(items)}] {item_name}: {e.message}")
                        if errors:
                            typer.echo(f"Validation failed ({len(errors)} errors):", err=True)
                            for e in errors[:10]:
                                typer.echo(e, err=True)
                            if len(errors) > 10:
                                typer.echo(f"  ... and {len(errors) - 10} more", err=True)
                            raise typer.Exit(1)
                        typer.echo(f"  All {len(items)} items valid.")
                else:
                    typer.echo(f"Agent '{agent}' has no taskInputSchema, skipping validation")

    # Upload
    typer.echo(f"Uploading {len(items)} items as dataset '{name}'...")

    client = create_client(host, api_key)
    payload: dict = {
        "name": name,
        "items": [{"input": item} for item in items],
    }
    if description:
        payload["description"] = description

    response = client.post("/api/public/datasets/import", json=payload)

    if response.status_code in (200, 201):
        data = response.json()
        typer.echo(f"Dataset created!")
        typer.echo(f"  Dataset ID: {data.get('datasetId')}")
        typer.echo(f"  Name:       {data.get('datasetName')}")
        typer.echo(f"  Items:      {data.get('itemCount')}")
    else:
        typer.echo(f"Failed (HTTP {response.status_code}): {response.text}", err=True)
        raise typer.Exit(1)


def main() -> None:
    """Entry point for the `mantis` CLI command."""
    app()


if __name__ == "__main__":
    main()
