#!/usr/bin/env python3
"""
Enterprise Agent-QA CLI Entrypoint
----------------------------------
Provides a centralized command-line interface for the agent-skills ecosystem.
Dispatches commands to underlying specialized tools in tools/.
"""

import os
import sys
import argparse

# Enforce UTF-8 stdout encoding for Windows console compatibility
if hasattr(sys.stdout, 'reconfigure'):
    try:
        sys.stdout.reconfigure(encoding='utf-8')
        sys.stderr.reconfigure(encoding='utf-8')
    except Exception:
        pass

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
REPO_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
sys.path.insert(0, SCRIPT_DIR)


def cmd_version(args):
    manifest_path = os.path.join(REPO_ROOT, "skills_manifest.json")
    version = "2.1.1"
    if os.path.exists(manifest_path):
        try:
            import json
            with open(manifest_path, "r", encoding="utf-8") as f:
                data = json.load(f)
                version = data.get("version", version)
        except Exception:
            pass
    print(f"Agent-QA Hub CLI v{version}")
    manifest_skills = 27
    if os.path.exists(manifest_path):
        try:
            import json
            with open(manifest_path, "r", encoding="utf-8") as f:
                manifest_skills = json.load(f).get("totalSkills", 27)
        except Exception:
            pass
    print(f"Catalog: {manifest_skills} Standardized Skills | 6 Subagent Swarm Profiles")


def cmd_verify(args):
    from verify_all import main as verify_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(verify_main())


def cmd_lint(args):
    from infer_linter import main as lint_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(lint_main())


def cmd_heal(args):
    from cache_resolver import main as heal_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(heal_main())


def cmd_distill(args):
    from distill_log import main as distill_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(distill_main())


def cmd_impact(args):
    from test_impact import main as impact_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(impact_main())


def cmd_clean(args):
    """Cleans temporary build files, __pycache__, and compiled bytecode across the repository."""
    import shutil
    removed_count = 0
    print("🧹 Cleaning temporary build artifacts and bytecode caches...")
    for root, dirs, files in os.walk(REPO_ROOT):
        for d in list(dirs):
            if d == "__pycache__":
                full_dir = os.path.join(root, d)
                try:
                    shutil.rmtree(full_dir)
                    removed_count += 1
                    dirs.remove(d)
                except Exception as e:
                    print(f"⚠️  Could not remove {full_dir}: {e}")
        for f in files:
            if f.endswith((".pyc", ".pyo", ".tmp")):
                try:
                    os.remove(os.path.join(root, f))
                    removed_count += 1
                except Exception:
                    pass
    print(f"✅ Cleanup finished: {removed_count} build artifacts and caches removed.")


def cmd_flaky(args):
    from flaky_detector import main as flaky_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(flaky_main())


def cmd_canary(args):
    from agent_canary import main as canary_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(canary_main())


def cmd_analyze(args):
    from analyze_project import main as analyze_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(analyze_main())


def cmd_setup(args):
    from setup_project import main as setup_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(setup_main())


def cmd_auto_heal(args):
    from auto_heal import main as auto_heal_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(auto_heal_main())


def cmd_safety_check(args):
    from safety_firewall import main as safety_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(safety_main())


def cmd_contract_drift(args):
    from api_drift_sentinel import main as drift_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(drift_main())


def cmd_mcp_serve(args):
    from mcp_server import run_stdio_server
    run_stdio_server()


def cmd_mutate(args):
    from micro_mutator import main as mut_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(mut_main())


def cmd_heal_pr(args):
    from heal_pr_automator import main as pr_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(pr_main())


def cmd_shard(args):
    from matrix_sharder import main as shard_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(shard_main())


def cmd_transpile(args):
    from omni_transpiler import transpile_file
    res = transpile_file(args.source, args.to, args.out)
    print(f"✅ Transpiled '{args.source}' -> {args.to} ({len(res)} files):")
    for p, code in res.items():
        print(f"  • {p} ({len(code.splitlines())} lines)")
        if not args.out:
            print("---")
            print(code)
            print("---")


def cmd_omni_scan(args):
    from omni_transpiler import scan_repository
    inv = scan_repository(args.path)
    import json
    if getattr(args, "json", False):
        print(json.dumps(inv, indent=2))
    else:
        print("======================================================================")
        print("  OMNI-AUTOMATION POLYGLOT REPOSITORY INVENTORY (0-TOKEN AST SCAN)")
        print("======================================================================")
        print(f"📁 Target Directory   : {inv['repository']}")
        print(f"📄 Scanned Files      : {inv['total_files_scanned']}")
        print(f"🌐 Languages Detected : {inv['languages']}")
        print(f"⚡ Frameworks         : {inv['frameworks']}")
        print(f"🏛️ Page Classes Found : {len(inv['pages'])}")
        print(f"🎯 Total Locators     : {inv['total_locators']}")
        print(f"⚙️ Action Methods     : {inv['total_methods']}")
        print(f"🧪 Test Cases Found   : {len(inv['test_cases'])}")
        print("======================================================================")


def cmd_report_bug(args):
    from bug_reporter import main as bug_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(bug_main())


def cmd_mock_shield(args):
    from network_mock_shield import main as mock_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(mock_main())


def cmd_visual_mask(args):
    from smart_visual_masker import main as mask_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(mask_main())


def cmd_ci_hook(args):
    from ci_healing_hook import main as hook_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(hook_main())


def cmd_migrate(args):
    """Migrates an entire legacy test suite (Katalon, Cypress, Selenium) to modern target (Playwright TS)."""
    from omni_transpiler import transpile_file
    src = getattr(args, "src", None)
    dest = getattr(args, "dest", None) or "playwright_suite"
    target = getattr(args, "target", "playwright-ts")
    if not src:
        print("❌ Error: --src parameter is required for migrate.")
        sys.exit(1)

    if os.path.isdir(src):
        migrated_count = 0
        test_exts = {".groovy", ".java", ".js", ".ts", ".py"}
        for root, _, files in os.walk(src):
            for f in files:
                if os.path.splitext(f)[1].lower() in test_exts:
                    file_path = os.path.join(root, f)
                    try:
                        transpile_file(file_path, target, output_dir=dest, hermetic=True)
                        migrated_count += 1
                    except Exception:
                        pass
        print(f"✅ [MIGRATION COMPLETE] {migrated_count} test files converted to {target} in '{dest}'.")
    else:
        transpile_file(src, target, output_dir=dest, hermetic=True)
        print(f"✅ [MIGRATION COMPLETE] '{src}' -> {target} in '{dest}'.")


def cmd_decouple_config(args):
    from config_decoupler import main as decouple_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(decouple_main())


def cmd_fuzz_api(args):
    from api_fuzzer import main as fuzz_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(fuzz_main())


def cmd_som(args):
    from visual_som_helper import main as som_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(som_main())


def cmd_export_postman(args):
    from export_postman_collection import main as postman_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(postman_main())


def cmd_generate_jmx(args):
    from generate_jmx_plan import main as jmx_main
    sys.argv = [sys.argv[0]] + (sys.argv[2:] if len(sys.argv) > 2 else [])
    sys.exit(jmx_main())


def main():
    parser = argparse.ArgumentParser(
        prog="agent-qa",
        description="Enterprise AI Agent Quality Engineering & Test Automation Hub CLI"
    )
    parser.add_argument(
        "--offline", "--offline-deterministic",
        action="store_true",
        dest="offline",
        help="Force 100%% offline air-gapped deterministic mode (zero external network / LLM API calls)"
    )
    subparsers = parser.add_subparsers(dest="subcommand", help="Available subcommands")

    # version
    p_ver = subparsers.add_parser("version", help="Show Hub version and catalog metadata")
    p_ver.set_defaults(func=cmd_version)

    # setup
    p_setup = subparsers.add_parser("setup", help="Autonomous 1-click bootstrap of target project (AI guide, Git hook, AST map)")
    p_setup.set_defaults(func=cmd_setup)

    # auto-heal
    p_auto_heal = subparsers.add_parser("auto-heal", help="Autonomous closed-loop test failure diagnosis and healing plan")
    p_auto_heal.set_defaults(func=cmd_auto_heal)

    # heal-pr
    p_heal_pr = subparsers.add_parser("heal-pr", help="Calculate self-healing confidence and generate Git PR draft")
    p_heal_pr.set_defaults(func=cmd_heal_pr)

    # mutate
    p_mut = subparsers.add_parser("mutate", help="Inject AST micro-mutants to verify test assertion strength")
    p_mut.set_defaults(func=cmd_mutate)

    # shard
    p_shard = subparsers.add_parser("shard", help="Balance parallel CI/CD test execution using duration-balanced bin-packing")
    p_shard.set_defaults(func=cmd_shard)

    # safety-check
    p_safety = subparsers.add_parser("safety-check", help="Action Safety Firewall & Blast Radius Containment")
    p_safety.set_defaults(func=cmd_safety_check)

    # contract-drift
    p_drift = subparsers.add_parser("contract-drift", help="Detect breaking API changes across OpenAPI / Swagger specs")
    p_drift.set_defaults(func=cmd_contract_drift)

    # mcp-serve
    p_mcp = subparsers.add_parser("mcp-serve", help="Start the JSON-RPC 2.0 stdio Model Context Protocol (MCP) server")
    p_mcp.set_defaults(func=cmd_mcp_serve)

    # analyze
    p_analyze = subparsers.add_parser("analyze", help="Execute Comprehensive Project Health & AST Analysis")
    p_analyze.set_defaults(func=cmd_analyze)

    # verify
    p_verify = subparsers.add_parser("verify", help="Execute 7 Quality Gates DoD Verification")
    p_verify.set_defaults(func=cmd_verify)

    # lint
    p_lint = subparsers.add_parser("lint", help="Run AST Fake-Pass & Zero-Assertion Sentinel")
    p_lint.set_defaults(func=cmd_lint)

    # heal
    p_heal = subparsers.add_parser("heal", help="Query, learn, or record-failed into semantic memory cache")
    p_heal.set_defaults(func=cmd_heal)

    # distill
    p_distill = subparsers.add_parser("distill", help="Distill and filter verbose CLI logs by 95 percent")
    p_distill.set_defaults(func=cmd_distill)

    # impact
    p_impact = subparsers.add_parser("impact", help="Analyze code change impact using AST Knowledge Graph")
    p_impact.set_defaults(func=cmd_impact)

    # canary
    p_canary = subparsers.add_parser("canary", help="Run autonomous canary resilience benchmark")
    p_canary.set_defaults(func=cmd_canary)

    # clean
    p_clean = subparsers.add_parser("clean", help="Clean build artifacts, __pycache__, and bytecode")
    p_clean.set_defaults(func=cmd_clean)

    # flaky
    p_flaky = subparsers.add_parser("flaky", help="Detect flaky tests and manage quarantine lifecycle")
    p_flaky.set_defaults(func=cmd_flaky)

    # transpile
    p_trans = subparsers.add_parser("transpile", help="Transpile test files across languages and frameworks (0-token)")
    p_trans.add_argument("source", help="Source file path")
    p_trans.add_argument("--to", required=True, choices=["playwright-ts", "playwright-python", "selenium-java", "katalon-groovy", "cypress-js"], help="Target framework")
    p_trans.add_argument("--out", default=None, help="Output directory")
    p_trans.set_defaults(func=cmd_transpile)

    # omni-scan
    p_omni = subparsers.add_parser("omni-scan", help="Scan repository and build polyglot test inventory (0-token)")
    p_omni.add_argument("path", default=".", nargs="?", help="Directory to scan")
    p_omni.add_argument("--json", action="store_true", help="Output raw JSON")
    p_omni.set_defaults(func=cmd_omni_scan)

    # report-bug
    p_bug = subparsers.add_parser("report-bug", help="Auto-generate enterprise Jira/Markdown Bug Ticket with RCA")
    p_bug.set_defaults(func=cmd_report_bug)

    # mock-shield
    p_mock = subparsers.add_parser("mock-shield", help="Generate API Route Interception & Chaos Fault Injection Shield")
    p_mock.set_defaults(func=cmd_mock_shield)

    # visual-mask
    p_mask = subparsers.add_parser("visual-mask", help="Smart Visual Masking & Anti-Flake Animation Blocker")
    p_mask.set_defaults(func=cmd_visual_mask)

    # ci-hook
    p_ci = subparsers.add_parser("ci-hook", help="CI/CD Silent Self-Healing Pipeline Hook & Automated PR Generator")
    p_ci.set_defaults(func=cmd_ci_hook)

    # migrate
    p_migrate = subparsers.add_parser("migrate", help="Autonomous suite migration from legacy (Katalon/Cypress) to modern Playwright")
    p_migrate.add_argument("--src", required=True, help="Source legacy test suite file or directory")
    p_migrate.add_argument("--dest", default="playwright_suite", help="Destination directory for migrated code")
    p_migrate.add_argument("--target", default="playwright-ts", choices=["playwright-ts", "playwright-python", "selenium-java", "cypress-js"], help="Target framework")
    p_migrate.set_defaults(func=cmd_migrate)

    # decouple-config
    p_decouple = subparsers.add_parser("decouple-config", help="Decouple hardcoded URLs, credentials, and secrets into env profiles")
    p_decouple.add_argument("target", nargs="?", default=os.getcwd(), help="Target directory to scan")
    p_decouple.add_argument("--fix", action="store_true", help="Apply environment variable replacements in-place")
    p_decouple.add_argument("--out-dir", default=None, help="Output directory for config profiles")
    p_decouple.set_defaults(func=cmd_decouple_config)

    # fuzz-api
    p_fuzz = subparsers.add_parser("fuzz-api", help="Autonomous API Fuzzing & Chaos Breaker (bắn mutation payloads, bắt 500, DB leak, SLA breach)")
    p_fuzz.set_defaults(func=cmd_fuzz_api)

    # som (Visual Grounding for Canvas / GIS)
    p_som = subparsers.add_parser("som", help="Multimodal Vision Grounding & Set-of-Marks (SoM) for Canvas/GIS without DOM")
    p_som.set_defaults(func=cmd_som)

    # export-postman
    p_postman = subparsers.add_parser("export-postman", help="Export OpenAPI / Swagger / HAR to Postman Collection v2.1.0 with auto-asserts")
    p_postman.set_defaults(func=cmd_export_postman)

    # generate-jmx
    p_jmx = subparsers.add_parser("generate-jmx", help="Generate Apache JMeter 5.x Test Plan XML (.jmx) from OpenAPI / Postman / HAR")
    p_jmx.set_defaults(func=cmd_generate_jmx)

    args, unknown = parser.parse_known_args()
    if getattr(args, "offline", False):
        os.environ["AGENT_QA_OFFLINE"] = "1"
        print("🔒 [OFFLINE MODE] Air-Gapped Deterministic Mode Active. 100% Local AST & Heuristics (Zero External API calls).")

    if not args.subcommand:
        parser.print_help()
        sys.exit(0)

    # Dispatch subcommand
    args.func(args)


if __name__ == "__main__":
    main()
