import sys
import os
import json
import argparse
from typing import Any, Dict, List, Optional

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from core.problem_spec import ProblemSpec, ProblemType
from pipeline import UniversalSolver
from interface.nl_parser import parse_description, parse_solve_input
from core.explainer import Explainer


def create_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="aalgoi",
        description="AAlgoI Universal Problem Solver CLI"
    )
    sub = parser.add_subparsers(dest="command", help="Command to run")

    solve_parser = sub.add_parser("solve", help="Solve a problem from natural language")
    solve_parser.add_argument("description", type=str, help="Natural language problem description")
    solve_parser.add_argument("--data", type=str, default=None, help="Input data as JSON")
    solve_parser.add_argument("--llm", action="store_true", help="Use LLM for synthesis")
    solve_parser.add_argument("--json", action="store_true", help="Output as JSON")

    spec_parser = sub.add_parser("solve-spec", help="Solve a problem from JSON ProblemSpec")
    spec_parser.add_argument("spec_file", type=str, help="Path to ProblemSpec JSON file")
    spec_parser.add_argument("--data", type=str, default=None, help="Input data as JSON")
    spec_parser.add_argument("--llm", action="store_true", help="Use LLM for synthesis")
    spec_parser.add_argument("--json", action="store_true", help="Output as JSON")

    explain_parser = sub.add_parser("explain", help="Explain an algorithm")
    explain_parser.add_argument("algorithm", type=str, help="Algorithm name")
    explain_parser.add_argument("--detail", choices=["short", "detailed"], default="short")
    explain_parser.add_argument("--json", action="store_true", help="Output as JSON")

    stats_parser = sub.add_parser("stats", help="Show solver statistics")
    stats_parser.add_argument("--json", action="store_true", help="Output as JSON")

    web_parser = sub.add_parser("web", help="Launch Gradio web UI")
    web_parser.add_argument("--port", type=int, default=7860, help="Port number")
    web_parser.add_argument("--share", action="store_true", help="Generate public link")

    api_parser = sub.add_parser("api", help="Launch FastAPI REST API")
    api_parser.add_argument("--port", type=int, default=8000, help="Port number")
    api_parser.add_argument("--host", type=str, default="0.0.0.0", help="Host address")

    return parser


def cmd_solve(args, solver: UniversalSolver):
    spec = parse_description(args.description)
    data = None
    if args.data:
        try:
            data = json.loads(args.data)
        except json.JSONDecodeError:
            print(f"Error: --data must be valid JSON")
            sys.exit(1)
    if data is None:
        _, data = parse_solve_input(args.description)
    if data is None:
        data = [3, 1, 4, 1, 5]

    result = solver.solve(spec, data, use_llm=args.llm)

    if args.json:
        _print_json_result(result)
    else:
        _print_result(result)


def cmd_solve_spec(args, solver: UniversalSolver):
    try:
        with open(args.spec_file, "r") as f:
            spec_dict = json.load(f)
    except (FileNotFoundError, json.JSONDecodeError) as e:
        print(f"Error reading spec file: {e}")
        sys.exit(1)

    spec = ProblemSpec.from_dict(spec_dict)
    data = None
    if args.data:
        try:
            data = json.loads(args.data)
        except json.JSONDecodeError:
            data = [3, 1, 2]
    else:
        data = [3, 1, 2]

    result = solver.solve(spec, data, use_llm=args.llm)

    if args.json:
        _print_json_result(result)
    else:
        _print_result(result)


def cmd_explain(args):
    explainer = Explainer()
    exp = explainer.explain(args.algorithm, detail=args.detail)

    if args.json:
        print(json.dumps({
            "algorithm": exp.algorithm_name,
            "summary": exp.summary,
            "complexity": exp.complexity,
            "steps": exp.steps,
            "best_for": exp.best_for,
            "source": exp.source
        }, indent=2))
    else:
        print(f"\n{'='*60}")
        print(f"  {exp.algorithm_name}")
        print(f"{'='*60}")
        print(f"\n  {exp.summary}")
        print(f"\n  Complexity: {exp.complexity}")
        print(f"\n  Best For: {exp.best_for}")
        if exp.steps:
            print(f"\n  Steps:")
            for i, step in enumerate(exp.steps, 1):
                print(f"    {i}. {step}")
        print()


def cmd_stats(args, solver: UniversalSolver):
    stats = solver.get_stats()
    if args.json:
        print(json.dumps(stats, indent=2))
    else:
        print(f"\n{'='*60}")
        print(f"  AAlgoI Statistics")
        print(f"{'='*60}")
        print(f"  Total solves: {stats.get('total_solves', 0)}")
        vstats = stats.get("validator", {})
        print(f"  Validations: {vstats.get('total_validations', 0)} total, "
              f"{vstats.get('failed', 0)} failed")
        print()


def cmd_web(args):
    try:
        from interface.web_ui import launch
        launch(share=args.share, server_port=args.port)
    except ImportError:
        print("Gradio not available. Install with: pip install gradio")
        sys.exit(1)


def cmd_api(args):
    try:
        from interface.api import run
        run(host=args.host, port=args.port)
    except ImportError:
        print("FastAPI not available. Install with: pip install fastapi uvicorn")
        sys.exit(1)


def _print_result(result: Dict[str, Any]):
    print(f"\n{'='*60}")
    print(f"  Result")
    print(f"{'='*60}")
    r = result.get("result")
    if isinstance(r, list) and len(r) > 20:
        print(f"  Output: {r[:20]} ... ({len(r)} total)")
    else:
        print(f"  Output: {r}")
    print(f"  Success: {result.get('success', False)}")
    print(f"  Time: {result.get('time_ms', 0):.2f} ms")
    print(f"  Pipeline: {' -> '.join(result.get('pipeline', []))}")

    sel = result.get("selection", {})
    print(f"  Strategy: {sel.get('synthesis_strategy', 'unknown')}")
    print(f"  Confidence: {sel.get('confidence', 0):.2f}")

    validation = result.get("validation", [])
    if validation:
        print(f"\n  Validation:")
        for v in validation:
            status = "[OK]" if v.get("passed") else "[FAIL]"
            print(f"    {status} {v['algorithm']}")

    explanations = result.get("explanation", [])
    if explanations:
        print(f"\n  Explanation:")
        for exp in explanations:
            print(f"    {exp.algorithm_name}: {exp.summary[:100]}...")
    print()


def _print_json_result(result: Dict[str, Any]):
    output = {
        "success": result.get("success"),
        "result": _serialize_for_json(result.get("result")),
        "time_ms": result.get("time_ms"),
        "pipeline": result.get("pipeline"),
        "strategy": result.get("selection", {}).get("synthesis_strategy"),
        "confidence": result.get("selection", {}).get("confidence"),
    }
    print(json.dumps(output, indent=2))


def _serialize_for_json(obj: Any) -> Any:
    if isinstance(obj, (int, float, str, bool, type(None))):
        return obj
    if isinstance(obj, (list, tuple)):
        if len(obj) > 20:
            return list(obj[:20]) + ["..."]
        return list(obj)
    if isinstance(obj, dict):
        return {str(k): _serialize_for_json(v) for k, v in obj.items()}
    return str(obj)


def main(argv: Optional[List[str]] = None):
    parser = create_parser()
    args = parser.parse_args(argv)

    if args.command is None:
        parser.print_help()
        return

    if args.command in ("explain",):
        cmd_explain(args)
        return

    solver = UniversalSolver()

    commands = {
        "solve": cmd_solve,
        "solve-spec": cmd_solve_spec,
        "stats": cmd_stats,
        "web": cmd_web,
        "api": cmd_api,
    }

    cmd_fn = commands.get(args.command)
    if cmd_fn:
        cmd_fn(args, solver)


if __name__ == "__main__":
    main()
