#!/usr/bin/env python3
"""
TOXO Command Line Interface

Provides command-line tools for working with .toxo files.
"""

import sys
import argparse
import os
import subprocess
import json
import zipfile
import re
from pathlib import Path
from typing import Optional, List, Dict, Tuple, Any

from .client import DEFAULT_API_URL
from .utils import (
    get_version,
    check_dependencies,
    print_dependency_status,
    validate_toxo_file,
    print_toxo_info,
    get_sample_usage,
    get_help_text,
)


def _resolve_api_url(api_url: Optional[str]) -> str:
    """Resolve API URL from CLI arg, env, or default."""
    return (api_url or os.getenv("TOXO_API_URL") or DEFAULT_API_URL).rstrip("/")


def _require_api_key(explicit_key: Optional[str]) -> Optional[str]:
    """Resolve API key from CLI arg or env."""
    return explicit_key or os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")


def cmd_version():
    """Print version information."""
    print(f"🧠 TOXO v{get_version()}")
    print("Smart Layer for Context Augmented Language Models (CALM)")
    print("https://toxotune.com")


def cmd_check():
    """Check dependencies and system status."""
    print("🔍 TOXO CALM Layer Dependency Check")
    print("=" * 40)
    deps = check_dependencies()
    print(f"Python Version: {deps.get('python_version')}")
    print(f"Status: {deps.get('status')}")
    print("")
    print("Required packages:")
    for pkg, status in deps.get("required_packages", {}).items():
        print(f"  - {pkg}: {status}")
    print("")
    print("Optional packages:")
    for pkg, status in deps.get("optional_packages", {}).items():
        print(f"  - {pkg}: {status}")


def cmd_info(file_path: str):
    """Show information about a .toxo file."""
    if not file_path:
        print("❌ Error: Please specify a .toxo file path")
        return
    
    print_toxo_info(file_path)


def cmd_validate(file_path: str):
    """Validate a .toxo file."""
    if not file_path:
        print("❌ Error: Please specify a .toxo file path")
        return
    
    result = validate_toxo_file(file_path)
    
    if result["valid"]:
        print(f"✅ {Path(file_path).name} is a valid TOXO file")
        if result["warnings"]:
            print("\n⚠️ Warnings:")
            for warning in result["warnings"]:
                print(f"  • {warning}")
    else:
        print(f"❌ {Path(file_path).name} is not a valid TOXO file")
        print("\n🚫 Errors:")
        for error in result["errors"]:
            print(f"  • {error}")


def cmd_sample():
    """Print sample usage code."""
    print("📖 TOXO Sample Usage Code:")
    print("=" * 40)
    print(get_sample_usage())


def cmd_help():
    """Print help information."""
    print(get_help_text())


def cmd_classify_query(
    question: str,
    *,
    domain: str = "general",
    threshold: float = 0.6,
    json_output: bool = False,
):
    """Classify a query and compute domain-confidence routing signals."""
    if not question:
        print("❌ Error: Provide a question to classify.")
        return

    try:
        from .core.quality_scorer import (  # type: ignore
            classify_query,
            compute_domain_confidence,
            should_use_layer,
        )
    except Exception as e:
        print("❌ Quality scoring utilities not available.")
        print(f"   Import error: {e}")
        return

    qtype = classify_query(question, domain)
    conf = compute_domain_confidence(question, domain)
    use_layer, conf2 = should_use_layer(question, domain, threshold=threshold)
    out = {
        "domain": domain,
        "question": question,
        "query_type": qtype,
        "domain_confidence": round(float(conf), 3),
        "threshold": float(threshold),
        "should_use_layer": bool(use_layer),
        "should_use_layer_confidence": round(float(conf2), 3),
    }

    if json_output:
        print(json.dumps(out, indent=2, ensure_ascii=False))
        return

    print(f"🧭 Domain: {domain}")
    print(f"❓ Question: {question}")
    print(f"🏷️ Query type: {qtype}")
    print(f"📈 Domain confidence: {out['domain_confidence']}")
    print(f"💡 Should use layer (threshold {threshold}): {out['should_use_layer']}")


def cmd_analyze_response(
    question: str,
    response: str,
    *,
    domain: str = "general",
    scoring_mode: Optional[str] = None,
    json_output: bool = False,
):
    """Analyze response quality using TOXO's quality scorer."""
    if not question or not response:
        print("❌ Error: Provide both --question and --response.")
        return

    try:
        from .core.quality_scorer import analyze_response  # type: ignore
    except Exception as e:
        print("❌ Quality scoring utilities not available.")
        print(f"   Import error: {e}")
        return

    result = analyze_response(
        question,
        response,
        domain=domain,
        scoring_mode=scoring_mode,
        use_dynamic_weights=True,
    )

    if json_output:
        print(json.dumps(result, indent=2, ensure_ascii=False))
        return

    print(f"🧭 Domain: {domain}")
    print(f"❓ Question: {question}")
    print(f"🏷️ Query type: {result.get('query_type')}")
    print(f"🎛️ Scoring mode: {result.get('scoring_mode')}")
    print(f"⭐ Overall: {result.get('overall')} ({result.get('verdict')})")
    scores = result.get("scores") or {}
    if isinstance(scores, dict) and scores:
        print("📊 Scores:")
        for k in [
            "relevance",
            "completeness",
            "coherence",
            "conciseness",
            "domain_fit",
            "actionability",
            "info_density",
        ]:
            if k in scores:
                print(f"  - {k}: {scores[k]}")
    notes = result.get("notes") or []
    if notes:
        print("📝 Notes:")
        for n in notes:
            print(f"  - {n}")


def _read_zip_json(z: zipfile.ZipFile, name: str) -> Optional[dict]:
    try:
        raw = z.read(name)
    except Exception:
        return None
    try:
        return json.loads(raw.decode("utf-8"))
    except Exception:
        return None


def cmd_dump_layer(
    file_path: str,
    *,
    out_path: Optional[str] = None,
    pretty: bool = True,
):
    """Dump key .toxo internals (manifest/config/memory) to JSON."""
    if not file_path:
        print("❌ Error: Please specify a .toxo file path")
        return

    v = validate_toxo_file(file_path)
    if not v.get("valid"):
        print(f"❌ Invalid .toxo file: {v.get('errors')}")
        return

    fp = Path(file_path)
    payload: Dict[str, object] = {"file": str(fp), "info": v.get("info", {})}
    try:
        with zipfile.ZipFile(fp, "r") as z:
            names = set(z.namelist())
            payload["files"] = sorted(list(names))
            for candidate in [
                "manifest.json",
                "metadata.json",
                "layer_config.json",
                "config.json",
                "prompt_config.json",
                "memory/memory_state.json",
            ]:
                if candidate in names:
                    payload[candidate] = _read_zip_json(z, candidate)
    except Exception as e:
        print(f"❌ dump-layer failed: {e}")
        return

    text = json.dumps(payload, indent=2 if pretty else None, ensure_ascii=False)
    if out_path:
        out = Path(out_path)
        out.parent.mkdir(parents=True, exist_ok=True)
        out.write_text(text, encoding="utf-8")
        print(f"✅ Dump written to {out}")
    else:
        print(text)


def cmd_memory_info(
    file_path: str,
    *,
    json_output: bool = False,
):
    """Inspect memory_state.json (including static rules) inside a .toxo."""
    if not file_path:
        print("❌ Error: Please specify a .toxo file path")
        return

    v = validate_toxo_file(file_path)
    if not v.get("valid"):
        print(f"❌ Invalid .toxo file: {v.get('errors')}")
        return

    fp = Path(file_path)
    try:
        with zipfile.ZipFile(fp, "r") as z:
            state = _read_zip_json(z, "memory/memory_state.json") or {}
    except Exception as e:
        print(f"❌ memory-info failed: {e}")
        return

    training_contexts = state.get("training_contexts") if isinstance(state, dict) else None
    key_rules = state.get("key_rules") if isinstance(state, dict) else None
    example_phrases = state.get("example_phrases") if isinstance(state, dict) else None

    out = {
        "file": str(fp),
        "domain": state.get("domain") if isinstance(state, dict) else None,
        "memory_count": state.get("memory_count") if isinstance(state, dict) else None,
        "has_training_contexts": isinstance(training_contexts, list) and len(training_contexts) > 0,
        "training_contexts_count": len(training_contexts) if isinstance(training_contexts, list) else 0,
        "has_key_rules": isinstance(key_rules, list) and len(key_rules) > 0,
        "key_rules_count": len(key_rules) if isinstance(key_rules, list) else 0,
        "has_example_phrases": isinstance(example_phrases, dict) and len(example_phrases) > 0,
        "example_phrases_count": len(example_phrases) if isinstance(example_phrases, dict) else 0,
    }

    if json_output:
        print(json.dumps({"summary": out, "memory_state": state}, indent=2, ensure_ascii=False))
        return

    print(f"🧠 Layer: {fp.name}")
    if out.get("domain"):
        print(f"🧭 Domain: {out['domain']}")
    if out.get("memory_count") is not None:
        print(f"🗃️ Memory count: {out['memory_count']}")
    print(f"📚 training_contexts: {out['training_contexts_count']}")
    print(f"📌 key_rules: {out['key_rules_count']}")
    print(f"🧾 example_phrases: {out['example_phrases_count']}")


def cmd_query_local(
    layer_path: str,
    question: str,
    *,
    api_key: Optional[str] = None,
    llm_provider: str = "gemini",
    llm_model: Optional[str] = None,
    response_depth: Optional[str] = None,
):
    """Query a .toxo layer locally (no TOXO API). Requires toxo[local]."""
    if not layer_path or not question:
        print('❌ Error: Usage: toxo query-local LAYER.toxo "Question"')
        return

    api_key = _require_api_key(api_key)
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    try:
        from .core import ToxoLayer  # type: ignore
        import asyncio
    except Exception as e:
        print("❌ Local query requires toxo[local]. Install: pip install \"toxo[local]\"")
        print(f"   Import error: {e}")
        return

    try:
        layer = ToxoLayer.load(layer_path)
        layer.setup_api_key(api_key, model=llm_model, provider=llm_provider)
        resp = asyncio.run(layer.query(question, response_depth=response_depth))
        print(resp)
    except Exception as e:
        print(f"❌ query-local failed: {e}")


def _chunk_text(text: str, *, chunk_chars: int = 1600, overlap_chars: int = 200) -> List[str]:
    if not text:
        return []
    t = text.strip()
    if not t:
        return []
    if chunk_chars <= 0:
        return [t]
    chunks: List[str] = []
    start = 0
    n = len(t)
    while start < n:
        end = min(n, start + chunk_chars)
        chunk = t[start:end].strip()
        if chunk:
            chunks.append(chunk)
        if end >= n:
            break
        start = max(0, end - max(0, overlap_chars))
    return chunks


def cmd_index_docs(
    *,
    docs: str,
    index_dir: str,
    layer_path: Optional[str] = None,
    domain: Optional[str] = None,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    recursive: bool = True,
    pattern: Optional[str] = None,
):
    """
    Build a cloud-hosted vector index from docs using the TOXO API.

    This posts documents to `/v1/rag/index`; vectors are stored on the service, not local disk.
    """
    if not docs or not index_dir:
        print("❌ Error: Usage: toxo index-docs --docs PATH --index-dir INDEX_ID [--layer LAYER.toxo]")
        return

    docs_path = Path(docs)
    if not docs_path.exists():
        print(f"❌ Docs path not found: {docs_path}")
        return

    try:
        import base64
        import mimetypes
        import requests
    except Exception as e:
        print("❌ Cloud RAG indexing requires `requests`.")
        print(f"   Import error: {e}")
        return

    api_url = _resolve_api_url(api_url)

    files: List[Dict[str, Any]] = []

    def _gather_files(root: Path) -> List[Path]:
        if root.is_file():
            return [root]
        matched: List[Path] = []
        if recursive:
            for p in root.rglob(pattern or "*"):
                if p.is_file():
                    matched.append(p)
        else:
            for p in root.glob(pattern or "*"):
                if p.is_file():
                    matched.append(p)
        return matched

    for p in _gather_files(docs_path):
        try:
            raw = p.read_bytes()
        except Exception as e:
            print(f"⚠️ Skipping {p}: {e}")
            continue
        mt, _ = mimetypes.guess_type(str(p))
        files.append(
            {
                "path": str(p),
                "data_base64": base64.b64encode(raw).decode("ascii"),
                "mime_type": mt,
            }
        )

    if not files:
        print("⚠️ No files collected for indexing. Nothing to send.")
        return

    inferred_domain = None
    if layer_path:
        try:
            v = validate_toxo_file(layer_path)
            if v.get("valid"):
                inferred_domain = (v.get("info") or {}).get("domain")
        except Exception:
            inferred_domain = None

    effective_domain = (domain or inferred_domain or "general").strip() or "general"

    layer_b64 = None
    if layer_path:
        try:
            from .client import ToxoClient

            layer_b64 = ToxoClient(api_url=api_url)._layer_base64(layer_path)  # type: ignore[arg-type]
        except Exception:
            layer_b64 = None

    payload: Dict[str, Any] = {
        "index_id": index_dir,
        "domain": effective_domain,
        "docs": files,
        "layer_base64": layer_b64,
        "api_key": _require_api_key(api_key),
    }

    url = api_url.rstrip("/") + "/v1/rag/index"
    print(f"☁️  Sending {len(files)} document(s) to TOXO RAG index via {url} ...")
    try:
        resp = requests.post(url, json=payload, timeout=600)
    except Exception as e:
        print(f"❌ RAG index request failed: {e}")
        return

    if resp.status_code != 200:
        print(f"❌ RAG indexing failed: HTTP {resp.status_code} - {resp.text}")
        return

    data = resp.json()
    print(f"✅ Indexed documents into cloud index '{index_dir}'")
    if "summary" in data:
        print(f"   Summary: {data['summary']}")


def cmd_rag_query(
    *,
    layer_path: str,
    index_dir: str,
    question: str,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    llm_provider: str = "gemini",
    llm_model: Optional[str] = None,
    top_k: int = 5,
    response_depth: Optional[str] = None,
):
    """Query a layer with cloud-hosted retrieval using TOXO API."""
    if not layer_path or not index_dir or not question:
        print("❌ Error: Usage: toxo rag-query --layer LAYER.toxo --index-dir DIR --question \"...\"")
        return

    api_key = _require_api_key(api_key)
    if isinstance(api_key, str):
        api_key = api_key.strip()
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    api_url = _resolve_api_url(api_url)

    try:
        import requests
        from .client import ToxoClient
    except Exception as e:
        print("❌ Cloud RAG query requires `requests`.")
        print(f"   Import error: {e}")
        return

    client = ToxoClient(api_url=api_url)
    try:
        layer_b64 = client._layer_base64(layer_path)  # type: ignore[arg-type]
    except Exception as e:
        print(f"❌ Failed to read layer: {e}")
        return

    payload: Dict[str, Any] = {
        "layer_base64": layer_b64,
        "index_id": index_dir,
        "question": question,
        "api_key": api_key,
        "llm_provider": llm_provider,
        "llm_model": llm_model,
        "top_k": int(top_k),
        "response_depth": response_depth or "balanced",
    }

    url = api_url.rstrip("/") + "/v1/rag/query"
    print(f"☁️  Querying RAG index '{index_dir}' via {url} ...")
    try:
        resp = requests.post(url, json=payload, timeout=300)
    except Exception as e:
        print(f"❌ RAG query request failed: {e}")
        return

    if resp.status_code != 200:
        print(f"❌ RAG query failed: HTTP {resp.status_code} - {resp.text}")
        return

    data = resp.json()
    answer = data.get("answer") or data.get("response") or ""
    if not answer:
        print(data)
        return
    print(answer)


def cmd_agent_run(
    config_path: str,
    *,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    llm_provider: str = "gemini",
    llm_model: Optional[str] = None,
    json_out: Optional[str] = None,
):
    """
    Run a multi-agent workflow defined in a YAML/JSON config via the TOXO API.

    This calls the `/v1/agent/run` endpoint; the agent orchestration runs in the cloud.
    """
    if not config_path:
        print("❌ Error: Usage: toxo agent-run CONFIG.yaml")
        return

    api_key = _require_api_key(api_key)
    if isinstance(api_key, str):
        api_key = api_key.strip()
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    cfg_file = Path(config_path)
    if not cfg_file.exists():
        print(f"❌ Config not found: {cfg_file}")
        return

    try:
        import yaml  # type: ignore
    except Exception as e:
        print("❌ YAML support missing. Install: pip install pyyaml")
        print(f"   Import error: {e}")
        return

    try:
        raw_text = cfg_file.read_text(encoding="utf-8")
        spec = yaml.safe_load(raw_text) if cfg_file.suffix.lower() in (".yml", ".yaml") else json.loads(raw_text)
    except Exception as e:
        print(f"❌ Failed to parse config: {e}")
        return

    if not isinstance(spec, dict):
        print("❌ Invalid config: top-level must be a mapping/object.")
        return

    agents_spec = spec.get("agents") or []
    if not isinstance(agents_spec, list) or not agents_spec:
        print("❌ Invalid config: expected `agents: [...]` with at least one agent.")
        return

    try:
        import base64
        import requests
    except Exception as e:
        print("❌ Cloud agent-run requires `requests`.")
        print(f"   Import error: {e}")
        return

    api_url = _resolve_api_url(api_url)

    layers_payload: List[Dict[str, Any]] = []
    for a in agents_spec:
        if not isinstance(a, dict):
            continue
        agent_id = str(a.get("id") or a.get("name") or "").strip()
        layer_path = str(a.get("layer") or a.get("layer_path") or "").strip()
        if not agent_id or not layer_path:
            continue
        p = Path(layer_path)
        if not p.exists():
            print(f"⚠️ Skipping agent '{agent_id}': layer not found at {p}")
            continue
        try:
            raw = p.read_bytes()
        except Exception as e:
            print(f"⚠️ Skipping agent '{agent_id}': failed to read layer {p}: {e}")
            continue
        layers_payload.append(
            {
                "agent_id": agent_id,
                "layer_path": str(p),
                "layer_base64": base64.b64encode(raw).decode("ascii"),
            }
        )

    if not layers_payload:
        print("❌ No valid agents with readable layers found in config.")
        return

    payload: Dict[str, Any] = {
        "config": spec,
        "layers": layers_payload,
        "api_key": api_key,
        "llm_provider": llm_provider,
        "llm_model": llm_model,
    }

    url = api_url.rstrip("/") + "/v1/agent/run"
    print(f"☁️  Running agent workflow in cloud via {url} ...")
    try:
        resp = requests.post(url, json=payload, timeout=1800)
    except Exception as e:
        print(f"❌ agent-run request failed: {e}")
        return

    if resp.status_code != 200:
        print(f"❌ agent-run failed: HTTP {resp.status_code} - {resp.text}")
        return

    data = resp.json()
    final = data.get("final") or data.get("response") or ""

    if json_out:
        out = Path(json_out)
        out.parent.mkdir(parents=True, exist_ok=True)
        out.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
        print(f"✅ Full agent transcript written to {out}")

    if final:
        print(final)
    else:
        print(data)

def cmd_train_add_example(
    workspace: str,
    question: Optional[str] = None,
    answer: Optional[str] = None,
    jsonl: Optional[str] = None,
    domain: str = "general",
    task_type: str = "qa",
):
    """
    Add curated Q&A examples to a local training workspace.

    Two modes:
      - Single example via --question/--answer
      - Bulk import via --jsonl (one object per line)
    """
    if not workspace:
        print("❌ Error: Please provide a --workspace directory")
        return

    try:
        from .core.training_workspace import (
            add_example,
            add_examples_from_jsonl,
            ensure_workspace_dir,
        )
    except Exception as e:
        print("❌ Local training workspace helpers not available.")
        print(f"   Import error: {e}")
        return

    ws = ensure_workspace_dir(workspace)

    if jsonl:
        jsonl_path = Path(jsonl)
        if not jsonl_path.exists():
            print(f"❌ JSONL file not found: {jsonl_path}")
            return
        added = add_examples_from_jsonl(
            workspace_dir=ws,
            jsonl_path=jsonl_path,
            default_domain=domain,
            default_task_type=task_type,
        )
        print(f"✅ Added {added} examples from {jsonl_path} into {ws}")
        return

    if not question or not answer:
        print("❌ Error: Provide --question and --answer, or --jsonl")
        return

    out_path = add_example(
        workspace_dir=ws,
        question=question,
        answer=answer,
        domain=domain,
        task_type=task_type,
        source="user",
    )
    print(f"✅ Added example to {out_path}")


def cmd_train_auto(
    description: str,
    out_path: str,
    llm_provider: str = "gemini",
    llm_model: Optional[str] = None,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    num_examples: int = 5,
):
    """
    Train a TOXO layer from a natural language description using synthetic examples in the cloud.

    This calls the TOXO API `/v1/train_from_data` endpoint; no local heavy deps required.
    """
    if not description:
        print("❌ Error: Please provide a task description.")
        return
    if not out_path:
        print("❌ Error: Please provide an --out path for the trained .toxo file.")
        return

    api_key = _require_api_key(api_key)
    if isinstance(api_key, str):
        api_key = api_key.strip()
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    api_url = _resolve_api_url(api_url)

    try:
        import base64
        import requests
    except Exception as e:
        print("❌ Cloud training mode requires `requests`.")
        print(f"   Import error: {e}")
        return

    payload: Dict[str, Any] = {
        "description": description,
        "num_examples": int(num_examples),
        "llm_provider": llm_provider,
        "llm_model": llm_model,
        "api_key": api_key,
        # The backend is responsible for generating synthetic examples from the description.
        "mode": "auto_from_description",
    }
    url = api_url.rstrip("/") + "/v1/train_from_data"
    print(f"☁️  Running auto-training in cloud via {url} ...")
    try:
        resp = requests.post(url, json=payload, timeout=600)
    except Exception as e:
        print(f"❌ Cloud training request failed: {e}")
        return

    if resp.status_code != 200:
        print(f"❌ Cloud training failed: HTTP {resp.status_code} - {resp.text}")
        return

    data = resp.json()
    layer_b64 = data.get("layer_base64")
    if not layer_b64:
        print("❌ Cloud training response missing layer_base64.")
        return

    try:
        raw = base64.b64decode(layer_b64)
    except Exception as e:
        print(f"❌ Failed to decode layer_base64: {e}")
        return

    out = Path(out_path)
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_bytes(raw)
    summary = data.get("summary") or {}
    print(f"✅ Cloud-trained layer saved to {out}")
    if summary:
        print(f"   Summary: {summary}")


def cmd_train_from_docs(
    workspace: str,
    docs: str,
    recursive: bool = True,
    pattern: Optional[str] = None,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
):
    """
    Ingest a docs folder and store context-only snippets for training.

    Cloud-first: uploads docs to TOXO API for extraction, then writes contexts into the workspace.
    """
    if not workspace or not docs:
        print("❌ Error: Usage: toxo train-from-docs --workspace DIR --docs PATH")
        return

    docs_path = Path(docs)
    if not docs_path.exists():
        print(f"❌ Docs path not found: {docs_path}")
        return

    try:
        import base64
        import mimetypes
        import requests
        from .core.training_workspace import ensure_workspace_dir, add_contexts
    except Exception as e:
        print("❌ train-from-docs requires `requests` (client mode).")
        print(f"   Import error: {e}")
        return

    ws = ensure_workspace_dir(workspace)
    api_url = _resolve_api_url(api_url)

    def _gather_files(root: Path) -> List[Path]:
        if root.is_file():
            return [root]
        matched: List[Path] = []
        if recursive:
            for p in root.rglob(pattern or "*"):
                if p.is_file():
                    matched.append(p)
        else:
            for p in root.glob(pattern or "*"):
                if p.is_file():
                    matched.append(p)
        return matched

    files: List[Dict[str, Any]] = []
    for p in _gather_files(docs_path):
        try:
            raw = p.read_bytes()
        except Exception as e:
            print(f"⚠️ Skipping {p}: {e}")
            continue
        mt, _ = mimetypes.guess_type(str(p))
        files.append(
            {
                "path": str(p),
                "data_base64": base64.b64encode(raw).decode("ascii"),
                "mime_type": mt,
            }
        )

    if not files:
        print("⚠️ No docs collected. Nothing to upload.")
        return

    payload: Dict[str, Any] = {
        "index_id": "train_extract_contexts",
        "domain": "general",
        "api_key": _require_api_key(api_key),
        "docs": files,
    }
    url = api_url.rstrip("/") + "/v1/train/extract_contexts"
    print(f"☁️  Extracting contexts in cloud via {url} ...")
    try:
        resp = requests.post(url, json=payload, timeout=600)
    except Exception as e:
        print(f"❌ Cloud extraction request failed: {e}")
        return

    if resp.status_code != 200:
        print(f"❌ Cloud extraction failed: HTTP {resp.status_code} - {resp.text}")
        return

    data = resp.json()
    ctxs = data.get("contexts") or []
    contexts: List[Dict[str, Any]] = []
    for c in ctxs:
        t = (c or {}).get("text")
        if isinstance(t, str) and t.strip():
            contexts.append({"text": t.strip(), "source_file": None, "extension": None, "metadata": {"source": "cloud_extract"}})

    if not contexts:
        print("⚠️ No contexts returned from cloud extraction.")
        return

    added = add_contexts(workspace_dir=ws, contexts=contexts)
    print(f"✅ Added {added} contexts from docs into {ws} (extracted in cloud)")


def cmd_train_run(
    workspace: str,
    description: str,
    out_path: str,
    epochs: int = 10,
    llm_provider: str = "gemini",
    llm_model: Optional[str] = None,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
):
    """
    Run a simple training loop over stored examples and output a new .toxo.

    This:
      - loads examples from the workspace
      - optionally injects contexts as static training_contexts
      - runs ToxoTrainer.train_from_examples(...)
      - saves the resulting layer to --out
    """
    if not workspace or not out_path:
        print("❌ Error: Usage: toxo train-run --workspace DIR --out layer.toxo --description \"...\"")
        return

    api_key = _require_api_key(api_key)
    if isinstance(api_key, str):
        api_key = api_key.strip()
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    # Fail fast with a clear message instead of crashing inside ToxoConfig/GeminiConfig.
    if (llm_provider or "gemini") == "gemini" and api_key and not api_key.startswith("AIza"):
        print("❌ Error: Invalid Gemini API key format.")
        print("   Gemini keys typically start with 'AIza'.")
        print("   Pass a real key via --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY.")
        return

    # Resolve API URL (if provided). When api_url is set, we run training in the cloud.
    api_url = _resolve_api_url(api_url)

    try:
        from .core.training_workspace import (
            ensure_workspace_dir,
            load_workspace_examples,
            load_workspace_context_texts,
        )
    except Exception as e:
        print("❌ Local training workspace helpers not available.")
        print(f"   Import error: {e}")
        return

    ws = ensure_workspace_dir(workspace)
    examples_data = load_workspace_examples(ws)
    if not examples_data:
        print(f"❌ No examples found in {ws / 'examples.jsonl'}. Add some with `toxo train-add-example` first.")
        return

    # Map to minimal input/output pairs.
    io_examples = [
        {
            "input": ex["input_text"],
            "output": ex["expected_output"],
        }
        for ex in examples_data
    ]
    contexts = load_workspace_context_texts(ws, limit=200)
    domain = examples_data[0].get("domain", "general")

    # Cloud mode: call /v1/train_from_data on a TOXO API server.
    if api_url:
        try:
            import base64
            import requests
        except Exception as e:
            print("❌ Cloud training mode requires `requests`.")
            print(f"   Import error: {e}")
            return

        payload: Dict[str, Any] = {
            "description": description,
            "epochs": epochs,
            "llm_provider": llm_provider,
            "llm_model": llm_model,
            "api_key": api_key,
            "domain": domain,
            "examples": io_examples,
            "contexts": [{"text": c} for c in contexts],
        }
        url = api_url.rstrip("/") + "/v1/train_from_data"
        print(f"☁️  Running training in cloud via {url} ...")
        try:
            resp = requests.post(url, json=payload, timeout=600)
        except Exception as e:
            print(f"❌ Cloud training request failed: {e}")
            return
        if resp.status_code != 200:
            print(f"❌ Cloud training failed: HTTP {resp.status_code} - {resp.text}")
            return
        data = resp.json()
        layer_b64 = data.get("layer_base64")
        if not layer_b64:
            print("❌ Cloud training response missing layer_base64.")
            return
        try:
            raw = base64.b64decode(layer_b64)
        except Exception as e:
            print(f"❌ Failed to decode layer_base64: {e}")
            return
        out = Path(out_path)
        out.parent.mkdir(parents=True, exist_ok=True)
        out.write_bytes(raw)
        summary = data.get("summary") or {}
        print(f"✅ Cloud-trained layer saved to {out}")
        if summary:
            print(f"   Summary: {summary}")
        return

    # Local mode: fall back to original trainer-based implementation.
    try:
        import asyncio
        os.environ["GEMINI_API_KEY"] = api_key
        os.environ["GOOGLE_API_KEY"] = api_key
        from .core.config import ToxoConfig
        from .core.trainer import ToxoTrainer
        from .core.toxo_layer import ToxoLayer
        from .core.training_workspace import build_static_rules
    except Exception as e:
        print("❌ Local training requires toxo[local]. Install: pip install \"toxo[local]\"")
        print(f"   Import error: {e}")
        return

    config = ToxoConfig()
    trainer = ToxoTrainer(
        config=config,
        layer_name=f"trained_layer_{Path(out_path).stem}",
        domain=domain,
        description=description,
        llm_provider=llm_provider,
        llm_model=llm_model,
        llm_api_key=api_key,
    )

    static_rules = build_static_rules(
        examples=examples_data,
        contexts=contexts,
        max_training_contexts=20,
        max_example_phrases=30,
    )

    async def _run() -> None:
        if len(io_examples) < 2:
            print(f"⚠️ Only {len(io_examples)} example(s) found – skipping full training loop and creating a configured layer.")
            layer = ToxoLayer(
                config=config,
                name=f"trained_layer_{Path(out_path).stem}",
                domain=domain,
                description=description,
            )
        else:
            layer = await trainer.train_from_examples(
                examples=io_examples,
                description=description,
                epochs=epochs,
            )

        try:
            if getattr(layer, "memory", None):
                if not hasattr(layer.memory, "static_rules"):
                    layer.memory.static_rules = {}
                layer.memory.static_rules.update(static_rules)
        except Exception:
            pass
        out = Path(out_path)
        out.parent.mkdir(parents=True, exist_ok=True)
        layer.save(out)
        print(f"✅ Trained layer saved to {out}")

    try:
        asyncio.run(_run())
    except Exception as e:
        print(f"❌ train-run failed: {e}")


def cmd_test(
    file_path: str,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    local: bool = False,
):
    """Test a .toxo file. Uses TOXO API by default; pass --local for full local mode."""
    if not file_path:
        print("❌ Error: Please specify a .toxo file path")
        return

    api_url = _resolve_api_url(api_url)
    use_client = not local

    api_key = _require_api_key(api_key)
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    try:
        if use_client:
            from .client import ToxoClient
            from .utils import validate_toxo_file

            print(f"🧪 Testing {Path(file_path).name} via TOXO API...")
            v = validate_toxo_file(file_path)
            if not v["valid"]:
                print(f"❌ Invalid .toxo file: {v['errors']}")
                return
            info = v.get("info", {})
            domain = info.get("domain", "this domain")
            name = info.get("name", "model")

            client = ToxoClient(api_url=api_url)
            test_question = f"What can you tell me about {domain}?"
            print(f"📊 Model: {name} (Domain: {domain})")
            print(f"❓ Test question: {test_question}")
            response = client.query(file_path, test_question, api_key=api_key)
            print(f"💬 Response: {response[:200]}{'...' if len(response) > 200 else ''}")
            print("🎉 Test completed successfully (via TOXO API)!")
        else:
            from .core import ToxoLayer
            import asyncio

            print(f"🧪 Testing {Path(file_path).name} (local mode)...")
            layer = ToxoLayer.load(file_path)
            print("✅ Model loaded successfully")
            layer.setup_api_key(api_key)
            print("✅ API key configured")
            info = layer.get_info()
            test_question = f"What can you tell me about {info['domain']}?"
            print(f"📊 Model: {info['name']} (Domain: {info['domain']})")
            print(f"❓ Test question: {test_question}")
            response = asyncio.run(layer.query(test_question))
            print(f"💬 Response: {response[:200]}{'...' if len(response) > 200 else ''}")
            print("🎉 Test completed successfully!")
    except ImportError as e:
        if "ToxoLayer" in str(e) or "google.generativeai" in str(e):
            print("❌ Local mode requires toxo[local]. Install: pip install toxo[local]")
            print("   Or use --api-url to test via TOXO API (no extra deps).")
        else:
            print(f"❌ Test failed: {e}")
    except Exception as e:
        print(f"❌ Test failed: {str(e)}")


def cmd_ask_doc(
    layer_path: str,
    doc_path: str,
    question: str,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    mime_type: Optional[str] = None,
):
    """Ask a question about a document using TOXO API multimodal endpoint."""
    if not layer_path or not doc_path or not question:
        print('❌ Error: Usage: toxo ask-doc LAYER.toxo DOC_PATH "Question"')
        return

    api_url = _resolve_api_url(api_url)
    api_key = _require_api_key(api_key)
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    from .client import ToxoClient

    layer = Path(layer_path)
    if not layer.exists():
        print(f"❌ Error: Layer file not found: {layer}")
        return
    doc = Path(doc_path)
    if not doc.exists():
        print(f"❌ Error: Document file not found: {doc}")
        return

    try:
        print(f"🧪 Asking about {doc.name} via TOXO API...")
        client = ToxoClient(api_url=api_url, api_key=api_key)
        response = client.query_multimodal(
            layer=layer,
            question=question,
            document_path=doc,
            mime_type=mime_type,
        )
        print(f"💬 Response: {response}")
    except Exception as e:
        print(f"❌ ask-doc failed: {e}")


def cmd_ask_image(
    layer_path: str,
    image_path: str,
    question: str,
    api_key: Optional[str] = None,
    api_url: Optional[str] = None,
    mime_type: Optional[str] = None,
):
    """Ask a question about an image using TOXO API multimodal endpoint."""
    if not layer_path or not image_path or not question:
        print('❌ Error: Usage: toxo ask-image LAYER.toxo IMAGE_PATH "Question"')
        return

    api_url = _resolve_api_url(api_url)
    api_key = _require_api_key(api_key)
    if not api_key:
        print("❌ Error: No API key configured.")
        print("   Provide --api-key or set GEMINI_API_KEY / GOOGLE_API_KEY in your environment.")
        return

    from .client import ToxoClient

    layer = Path(layer_path)
    if not layer.exists():
        print(f"❌ Error: Layer file not found: {layer}")
        return
    img = Path(image_path)
    if not img.exists():
        print(f"❌ Error: Image file not found: {img}")
        return

    try:
        print(f"🧪 Asking about {img.name} via TOXO API...")
        client = ToxoClient(api_url=api_url, api_key=api_key)
        response = client.query_multimodal(
            layer=layer,
            question=question,
            image_path=img,
            mime_type=mime_type,
        )
        print(f"💬 Response: {response}")
    except Exception as e:
        print(f"❌ ask-image failed: {e}")


def cmd_feedback(
    layer_path: str,
    question: str,
    response: str,
    rating: Optional[float] = None,
    suggestions: Optional[List[str]] = None,
    api_url: Optional[str] = None,
):
    """Send feedback about a layer response to TOXO API."""
    if not layer_path or not question or not response:
        print('❌ Error: Usage: toxo feedback LAYER.toxo "Question" "Response" [--rating 0-10] [--suggest ...]')
        return

    api_url = _resolve_api_url(api_url)

    from .client import ToxoClient

    layer = Path(layer_path)
    if not layer.exists():
        print(f"❌ Error: Layer file not found: {layer}")
        return

    try:
        client = ToxoClient(api_url=api_url)
        ok = client.feedback(
            layer=layer,
            question=question,
            response=response,
            rating=rating,
            suggestions=suggestions or None,
        )
        if ok:
            print("✅ Feedback sent successfully.")
        else:
            print("⚠️ Feedback call completed but did not return success.")
    except Exception as e:
        print(f"❌ Feedback failed: {e}")


_USECASES: Dict[str, Dict[str, object]] = {
    "client": {
        "label": "Client only (default) - talk to TOXO API",
        "extras": [],
        "system": {},
    },
    "local": {
        "label": "Full local processing (ToxoLayer, doc conversion)",
        "extras": ["local"],
        "system": {},
    },
    "vision": {
        "label": "Multimodal PDFs/images (Gemini)",
        "extras": ["vision"],
        "system": {
            "darwin": ["brew install poppler  # required for pdf2image"],
        },
    },
    "rag": {
        "label": "RAG / vector store / retrieval",
        "extras": ["rag"],
        "system": {},
    },
    "enterprise": {
        "label": "Monitoring / caching (enterprise)",
        "extras": ["enterprise"],
        "system": {},
    },
    "distributed": {
        "label": "Distributed (Redis-backed features)",
        "extras": ["distributed"],
        "system": {},
    },
    "auth": {
        "label": "Auth/JWT (server-style features)",
        "extras": ["auth"],
        "system": {},
    },
    "payments": {
        "label": "Payments/subscriptions (Razorpay)",
        "extras": ["payments"],
        "system": {},
    },
    "full": {
        "label": "Everything (largest install)",
        "extras": ["full"],
        "system": {
            "darwin": ["brew install poppler  # for pdf2image"],
        },
    },
}


def _repo_root() -> Optional[Path]:
    # If running from a source checkout, toxo/cli.py is inside the repo.
    try:
        here = Path(__file__).resolve()
        root = here.parents[1]
        if (root / "pyproject.toml").exists():
            return root
    except Exception:
        pass
    return None


def _pip_install(*, extras: List[str], run: bool) -> Tuple[str, List[str]]:
    """
    Returns (install_cmd_str, errors).
    """
    extras_str = ",".join(extras)
    root = _repo_root()
    if root:
        cmd = [sys.executable, "-m", "pip", "install", "-e", f".[{extras_str}]"]
        cmd_str = " ".join(cmd)
        if not run:
            return cmd_str, []
        p = subprocess.run(cmd, cwd=str(root), capture_output=True, text=True)
        if p.returncode != 0:
            return cmd_str, [p.stderr.strip() or p.stdout.strip() or "pip failed"]
        return cmd_str, []

    # Installed package environment.
    cmd = [sys.executable, "-m", "pip", "install", f"toxo[{extras_str}]"]
    cmd_str = " ".join(cmd)
    if not run:
        return cmd_str, []
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        return cmd_str, [p.stderr.strip() or p.stdout.strip() or "pip failed"]
    return cmd_str, []


def cmd_install(usecase: Optional[str], *, run: bool = False):
    """
    Interactive dependency install helper (use-case based).

    NOTE: pip cannot prompt during installation; this is a post-install wizard.
    """
    if not usecase:
        print("🧩 Choose a TOXO use case to install:")
        keys = list(_USECASES.keys())
        for i, k in enumerate(keys, start=1):
            print(f"  {i}. {k:11} - {_USECASES[k]['label']}")
        try:
            raw = input("Enter number (or a usecase key): ").strip()
        except EOFError:
            raw = ""
        if raw.isdigit():
            idx = int(raw)
            if 1 <= idx <= len(keys):
                usecase = keys[idx - 1]
        else:
            usecase = raw or None

    if not usecase or usecase not in _USECASES:
        print("❌ Unknown use case.")
        print("   Valid:", ", ".join(_USECASES.keys()))
        return

    spec = _USECASES[usecase]
    extras = spec["extras"]  # type: ignore[assignment]
    cmd_str, errors = _pip_install(extras=extras, run=run)

    print(f"✅ Use case: {usecase} - {spec['label']}")
    print(f"📦 Python deps: `{cmd_str}`" if not run else f"📦 Installed via: `{cmd_str}`")

    sys_lines: List[str] = []
    system_map = spec.get("system", {})  # type: ignore[assignment]
    if isinstance(system_map, dict):
        if sys.platform.startswith("darwin"):
            sys_lines = system_map.get("darwin", [])  # type: ignore[assignment]
        elif sys.platform.startswith("linux"):
            sys_lines = system_map.get("linux", [])  # type: ignore[assignment]
        elif sys.platform.startswith("win"):
            sys_lines = system_map.get("win32", [])  # type: ignore[assignment]
    if sys_lines:
        print("🔧 System deps:")
        for line in sys_lines:
            print(f"   - {line}")

    if errors:
        print("❌ Install failed:")
        for e in errors:
            print(f"   - {e}")


def main():
    """Main CLI entry point."""
    parser = argparse.ArgumentParser(
        prog="toxo",
        description="TOXO - Smart Layer for Context Augmented Language Models (CALM) CLI",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  toxo version                    # Show version
  toxo check                      # Check dependencies  
  toxo info model.toxo            # Show model info
  toxo validate model.toxo        # Validate model file
  toxo test model.toxo --api-key KEY  # Test via TOXO API (or use --local)
  toxo sample                     # Show sample code
  toxo help                       # Show help

Visit https://toxotune.com to create your AI experts!
        """
    )
    
    subparsers = parser.add_subparsers(dest="command", help="Available commands")
    
    # Version command
    subparsers.add_parser("version", help="Show version information")
    
    # Check command  
    subparsers.add_parser("check", help="Check dependencies and system status")
    
    # Info command
    info_parser = subparsers.add_parser("info", help="Show information about a .toxo file")
    info_parser.add_argument("file", help="Path to .toxo file")
    
    # Validate command
    validate_parser = subparsers.add_parser("validate", help="Validate a .toxo file")
    validate_parser.add_argument("file", help="Path to .toxo file")
    
    # Test command
    test_parser = subparsers.add_parser("test", help="Test a .toxo file (uses TOXO API by default)")
    test_parser.add_argument("file", help="Path to .toxo file")
    test_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    test_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")
    test_parser.add_argument("--local", action="store_true", help="Run locally (requires pip install toxo[local])")

    # Train-auto command (local training from description)
    train_auto_parser = subparsers.add_parser(
        "train-auto",
        help="Train a TOXO layer from a natural language description via TOXO API",
    )
    train_auto_parser.add_argument("description", help="Task / expert description")
    train_auto_parser.add_argument(
        "--out",
        required=True,
        help="Output .toxo path for the trained layer",
    )
    train_auto_parser.add_argument(
        "--llm-provider",
        default="gemini",
        help="LLM provider (default: gemini)",
    )
    train_auto_parser.add_argument(
        "--llm-model",
        required=False,
        help="LLM model name (e.g. gemini-2.5-flash-lite)",
    )
    train_auto_parser.add_argument(
        "--api-key",
        required=False,
        help="API key for training (or set GEMINI_API_KEY / GOOGLE_API_KEY)",
    )
    train_auto_parser.add_argument(
        "--api-url",
        required=False,
        help="TOXO API URL for cloud training (or set TOXO_API_URL)",
    )
    train_auto_parser.add_argument(
        "--num-examples",
        type=int,
        default=5,
        help="Number of synthetic training examples to generate (default: 5)",
    )

    # Train-add-example command
    train_add_parser = subparsers.add_parser(
        "train-add-example",
        help="Add curated Q&A examples to a local training workspace",
    )
    train_add_parser.add_argument(
        "--workspace",
        required=True,
        help="Training workspace directory",
    )
    train_add_parser.add_argument(
        "--question",
        required=False,
        help="Question/input text for a single example",
    )
    train_add_parser.add_argument(
        "--answer",
        required=False,
        help="Answer/expected output for a single example",
    )
    train_add_parser.add_argument(
        "--jsonl",
        required=False,
        help="Path to JSONL file with examples ({question,input,input_text}/{answer,output,expected_output})",
    )
    train_add_parser.add_argument(
        "--domain",
        required=False,
        default="general",
        help="Domain label for examples (default: general)",
    )
    train_add_parser.add_argument(
        "--task-type",
        required=False,
        default="qa",
        help="Task type label for examples (default: qa)",
    )

    # Train-from-docs command (context-only ingest)
    train_from_docs_parser = subparsers.add_parser(
        "train-from-docs",
        help="Ingest a docs folder and add contexts to a training workspace",
    )
    train_from_docs_parser.add_argument(
        "--workspace",
        required=True,
        help="Training workspace directory",
    )
    train_from_docs_parser.add_argument(
        "--docs",
        required=True,
        help="Path to docs folder or single file",
    )
    train_from_docs_parser.add_argument(
        "--no-recursive",
        dest="recursive",
        action="store_false",
        help="Do not search docs directory recursively",
    )
    train_from_docs_parser.add_argument(
        "--pattern",
        required=False,
        help="Optional glob pattern to filter files (e.g. '*.md')",
    )
    train_from_docs_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    train_from_docs_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")
    train_from_docs_parser.set_defaults(recursive=True)

    # Train-run command
    train_run_parser = subparsers.add_parser(
        "train-run",
        help="Run training over stored examples/contexts and output a new .toxo via TOXO API",
    )
    train_run_parser.add_argument(
        "--workspace",
        required=True,
        help="Training workspace directory",
    )
    train_run_parser.add_argument(
        "--description",
        required=True,
        help="Task / expert description for the trained layer",
    )
    train_run_parser.add_argument(
        "--out",
        required=True,
        help="Output .toxo path for the trained layer",
    )
    train_run_parser.add_argument(
        "--epochs",
        type=int,
        default=10,
        help="Number of training epochs (default: 10)",
    )
    train_run_parser.add_argument(
        "--llm-provider",
        default="gemini",
        help="LLM provider for training (default: gemini)",
    )
    train_run_parser.add_argument(
        "--llm-model",
        required=False,
        help="LLM model name (e.g. gemini-2.5-flash-lite)",
    )
    train_run_parser.add_argument(
        "--api-key",
        required=False,
        help="API key for training (or set GEMINI_API_KEY / GOOGLE_API_KEY)",
    )
    train_run_parser.add_argument(
        "--api-url",
        required=False,
        help="TOXO API URL for cloud training (or set TOXO_API_URL)",
    )

    # Ask-doc command (multimodal document)
    ask_doc_parser = subparsers.add_parser("ask-doc", help="Ask a question about a document via TOXO API")
    ask_doc_parser.add_argument("layer", help="Path to .toxo layer file")
    ask_doc_parser.add_argument("document", help="Path to document file (PDF, DOCX, etc.)")
    ask_doc_parser.add_argument("question", help="Question to ask about the document")
    ask_doc_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    ask_doc_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")
    ask_doc_parser.add_argument("--mime-type", required=False, help="Optional MIME type override")

    # Ask-image command (multimodal image)
    ask_image_parser = subparsers.add_parser("ask-image", help="Ask a question about an image via TOXO API")
    ask_image_parser.add_argument("layer", help="Path to .toxo layer file")
    ask_image_parser.add_argument("image", help="Path to image file")
    ask_image_parser.add_argument("question", help="Question to ask about the image")
    ask_image_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    ask_image_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")
    ask_image_parser.add_argument("--mime-type", required=False, help="Optional MIME type override")

    # Feedback command
    feedback_parser = subparsers.add_parser("feedback", help="Send feedback about a layer response to TOXO API")
    feedback_parser.add_argument("layer", help="Path to .toxo layer file")
    feedback_parser.add_argument("question", help="Original question")
    feedback_parser.add_argument("response", help="Model response text")
    feedback_parser.add_argument("--rating", type=float, required=False, help="Optional rating 0-10")
    feedback_parser.add_argument(
        "--suggest",
        action="append",
        dest="suggestions",
        help="Suggestion string (can be passed multiple times)",
    )
    feedback_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")

    # Quality scoring commands (offline, no network)
    classify_query_parser = subparsers.add_parser(
        "classify-query",
        help="Classify a query and compute domain-confidence routing signals",
    )
    classify_query_parser.add_argument(
        "--question",
        required=True,
        help="Question to classify",
    )
    classify_query_parser.add_argument(
        "--domain",
        default="general",
        help="Domain string used for classification (default: general)",
    )
    classify_query_parser.add_argument(
        "--threshold",
        type=float,
        default=0.6,
        help="Threshold for should_use_layer routing (default: 0.6)",
    )
    classify_query_parser.add_argument(
        "--json",
        action="store_true",
        dest="json_output",
        help="Print machine-readable JSON output",
    )

    analyze_response_parser = subparsers.add_parser(
        "analyze-response",
        help="Analyze response quality (offline scoring)",
    )
    analyze_response_parser.add_argument(
        "--question",
        required=True,
        help="Original question",
    )
    analyze_response_parser.add_argument(
        "--response",
        required=True,
        help="Model response text to score",
    )
    analyze_response_parser.add_argument(
        "--domain",
        default="general",
        help="Domain string used for scoring (default: general)",
    )
    analyze_response_parser.add_argument(
        "--scoring-mode",
        required=False,
        help="Optional scoring mode override (e.g. domain_expert, generalist)",
    )
    analyze_response_parser.add_argument(
        "--json",
        action="store_true",
        dest="json_output",
        help="Print machine-readable JSON output",
    )

    # Layer introspection commands (offline, zip-level)
    dump_layer_parser = subparsers.add_parser(
        "dump-layer",
        help="Dump key .toxo internals (manifest/config/memory) to JSON",
    )
    dump_layer_parser.add_argument("file", help="Path to .toxo file")
    dump_layer_parser.add_argument(
        "--out",
        required=False,
        help="Optional output path for JSON dump (default: print to stdout)",
    )
    dump_layer_parser.add_argument(
        "--compact",
        action="store_true",
        help="Compact JSON (no indentation)",
    )

    memory_info_parser = subparsers.add_parser(
        "memory-info",
        help="Inspect memory_state.json inside a .toxo (static rules, counts)",
    )
    memory_info_parser.add_argument("file", help="Path to .toxo file")
    memory_info_parser.add_argument(
        "--json",
        action="store_true",
        dest="json_output",
        help="Print machine-readable JSON output (includes full memory_state.json)",
    )

    # Local query convenience command
    query_local_parser = subparsers.add_parser(
        "query-local",
        help="Query a .toxo layer locally (no TOXO API) (requires toxo[local])",
    )
    query_local_parser.add_argument("layer", help="Path to .toxo layer file")
    query_local_parser.add_argument("question", help="Question to ask")
    query_local_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    query_local_parser.add_argument("--llm-provider", default="gemini", help="LLM provider (default: gemini)")
    query_local_parser.add_argument("--llm-model", required=False, help="LLM model name (optional)")
    query_local_parser.add_argument(
        "--response-depth",
        required=False,
        help="Verbosity: concise | balanced | detailed",
    )

    # RAG commands (optional deps)
    index_docs_parser = subparsers.add_parser(
        "index-docs",
        help="Build a cloud-hosted vector index from docs via TOXO API",
    )
    index_docs_parser.add_argument("--docs", required=True, help="Docs folder or single file to index")
    index_docs_parser.add_argument("--index-dir", required=True, help="Cloud index identifier (string key)")
    index_docs_parser.add_argument("--layer", required=False, help="Optional .toxo layer path (used to infer domain)")
    index_docs_parser.add_argument("--domain", required=False, help="Optional domain override")
    index_docs_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    index_docs_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")
    index_docs_parser.add_argument("--no-recursive", dest="recursive", action="store_false", help="Do not scan docs recursively")
    index_docs_parser.add_argument("--pattern", required=False, help="Optional glob pattern (e.g. '*.md')")
    index_docs_parser.set_defaults(recursive=True)

    rag_query_parser = subparsers.add_parser(
        "rag-query",
        help="Query a layer with cloud-hosted retrieval via TOXO API",
    )
    rag_query_parser.add_argument("--layer", required=True, help="Path to .toxo layer file")
    rag_query_parser.add_argument("--index-dir", required=True, help="Cloud index identifier used in index-docs")
    rag_query_parser.add_argument("--question", required=True, help="Question to ask")
    rag_query_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    rag_query_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")
    rag_query_parser.add_argument("--llm-provider", default="gemini", help="LLM provider (default: gemini)")
    rag_query_parser.add_argument("--llm-model", required=False, help="LLM model name (optional)")
    rag_query_parser.add_argument("--top-k", type=int, default=5, help="Top-k retrieved chunks (default: 5)")
    rag_query_parser.add_argument("--response-depth", required=False, help="Verbosity: concise | balanced | detailed")

    # Agent orchestration (local)
    agent_run_parser = subparsers.add_parser(
        "agent-run",
        help="Run a multi-agent pipeline defined in a YAML/JSON config via TOXO API",
    )
    agent_run_parser.add_argument("config", help="Path to agent config YAML/JSON")
    agent_run_parser.add_argument("--api-key", required=False, help="API key (or set GEMINI_API_KEY)")
    agent_run_parser.add_argument("--api-url", required=False, help="TOXO API URL (or set TOXO_API_URL)")
    agent_run_parser.add_argument("--llm-provider", default="gemini", help="LLM provider (default: gemini)")
    agent_run_parser.add_argument("--llm-model", required=False, help="LLM model name (optional)")
    agent_run_parser.add_argument("--json-out", required=False, help="Optional path to write a JSON transcript")

    # Install command (dependency wizard)
    install_parser = subparsers.add_parser("install", help="Install optional deps by use case")
    install_parser.add_argument("usecase", nargs="?", help=f"One of: {', '.join(_USECASES.keys())}")
    install_parser.add_argument("--run", action="store_true", help="Actually run pip install (default: just print commands)")
    
    # Sample command
    subparsers.add_parser("sample", help="Show sample usage code")
    
    # Help command
    subparsers.add_parser("help", help="Show detailed help information")
    
    # Parse arguments
    args = parser.parse_args()
    
    # Handle commands
    if args.command == "version":
        cmd_version()
    elif args.command == "check":
        cmd_check()
    elif args.command == "info":
        cmd_info(args.file)
    elif args.command == "validate":
        cmd_validate(args.file)
    elif args.command == "test":
        cmd_test(
            args.file,
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
            local=getattr(args, "local", False),
        )
    elif args.command == "train-auto":
        cmd_train_auto(
            description=args.description,
            out_path=args.out,
            llm_provider=getattr(args, "llm_provider", "gemini"),
            llm_model=getattr(args, "llm_model", None),
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
            num_examples=getattr(args, "num_examples", 5),
        )
    elif args.command == "train-add-example":
        cmd_train_add_example(
            workspace=getattr(args, "workspace", None),
            question=getattr(args, "question", None),
            answer=getattr(args, "answer", None),
            jsonl=getattr(args, "jsonl", None),
            domain=getattr(args, "domain", "general"),
            task_type=getattr(args, "task_type", "qa"),
        )
    elif args.command == "train-from-docs":
        cmd_train_from_docs(
            workspace=getattr(args, "workspace", None),
            docs=getattr(args, "docs", None),
            recursive=getattr(args, "recursive", True),
            pattern=getattr(args, "pattern", None),
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
        )
    elif args.command == "train-run":
        cmd_train_run(
            workspace=getattr(args, "workspace", None),
            description=getattr(args, "description", ""),
            out_path=getattr(args, "out", ""),
            epochs=getattr(args, "epochs", 10),
            llm_provider=getattr(args, "llm_provider", "gemini"),
            llm_model=getattr(args, "llm_model", None),
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
        )
    elif args.command == "ask-doc":
        cmd_ask_doc(
            args.layer,
            args.document,
            args.question,
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
            mime_type=getattr(args, "mime_type", None),
        )
    elif args.command == "ask-image":
        cmd_ask_image(
            args.layer,
            args.image,
            args.question,
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
            mime_type=getattr(args, "mime_type", None),
        )
    elif args.command == "feedback":
        cmd_feedback(
            args.layer,
            args.question,
            args.response,
            rating=getattr(args, "rating", None),
            suggestions=getattr(args, "suggestions", None),
            api_url=getattr(args, "api_url", None),
        )
    elif args.command == "classify-query":
        cmd_classify_query(
            getattr(args, "question", ""),
            domain=getattr(args, "domain", "general"),
            threshold=getattr(args, "threshold", 0.6),
            json_output=getattr(args, "json_output", False),
        )
    elif args.command == "analyze-response":
        cmd_analyze_response(
            getattr(args, "question", ""),
            getattr(args, "response", ""),
            domain=getattr(args, "domain", "general"),
            scoring_mode=getattr(args, "scoring_mode", None),
            json_output=getattr(args, "json_output", False),
        )
    elif args.command == "dump-layer":
        cmd_dump_layer(
            getattr(args, "file", ""),
            out_path=getattr(args, "out", None),
            pretty=not getattr(args, "compact", False),
        )
    elif args.command == "memory-info":
        cmd_memory_info(
            getattr(args, "file", ""),
            json_output=getattr(args, "json_output", False),
        )
    elif args.command == "query-local":
        cmd_query_local(
            getattr(args, "layer", ""),
            getattr(args, "question", ""),
            api_key=getattr(args, "api_key", None),
            llm_provider=getattr(args, "llm_provider", "gemini"),
            llm_model=getattr(args, "llm_model", None),
            response_depth=getattr(args, "response_depth", None),
        )
    elif args.command == "index-docs":
        cmd_index_docs(
            docs=getattr(args, "docs", ""),
            index_dir=getattr(args, "index_dir", ""),
            layer_path=getattr(args, "layer", None),
            domain=getattr(args, "domain", None),
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
            recursive=getattr(args, "recursive", True),
            pattern=getattr(args, "pattern", None),
        )
    elif args.command == "rag-query":
        cmd_rag_query(
            layer_path=getattr(args, "layer", ""),
            index_dir=getattr(args, "index_dir", ""),
            question=getattr(args, "question", ""),
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
            llm_provider=getattr(args, "llm_provider", "gemini"),
            llm_model=getattr(args, "llm_model", None),
            top_k=getattr(args, "top_k", 5),
            response_depth=getattr(args, "response_depth", None),
        )
    elif args.command == "agent-run":
        cmd_agent_run(
            getattr(args, "config", ""),
            api_key=getattr(args, "api_key", None),
            api_url=getattr(args, "api_url", None),
            llm_provider=getattr(args, "llm_provider", "gemini"),
            llm_model=getattr(args, "llm_model", None),
            json_out=getattr(args, "json_out", None),
        )
    elif args.command == "install":
        cmd_install(getattr(args, "usecase", None), run=getattr(args, "run", False))
    elif args.command == "sample":
        cmd_sample()
    elif args.command == "help":
        cmd_help()
    else:
        # Show help if no command specified
        parser.print_help()


if __name__ == "__main__":
    main()
