import argparse
import os
import sys

from responseiq.__version__ import __version__
from responseiq.models.agent_state import AgentState
from responseiq.plugin_registry import PluginRegistry
from responseiq.telemetry import ConsoleTelemetry

telemetry = ConsoleTelemetry()

# ---------------------------------------------------------------------------
# ANSI colour helpers (degrade gracefully when stdout is not a tty)
# ---------------------------------------------------------------------------
_TTY = sys.stdout.isatty()


def _c(code: str, text: str) -> str:
    return f"\033[{code}m{text}\033[0m" if _TTY else text


def _green(t: str) -> str:
    return _c("32", t)


def _yellow(t: str) -> str:
    return _c("33", t)


def _cyan(t: str) -> str:
    return _c("36", t)


def _bold(t: str) -> str:
    return _c("1", t)


def _dim(t: str) -> str:
    return _c("2", t)


# ---------------------------------------------------------------------------
# `responseiq init` — interactive setup wizard
# ---------------------------------------------------------------------------


def _run_init() -> None:
    """Interactive wizard that writes a .env for the current project."""
    sep = "─" * 60
    print()
    print(_bold(_cyan("  ResponseIQ Init Wizard")))
    print(_dim(f"  {sep}"))
    print()

    env_path = _find_project_root() / ".env"
    if env_path.exists():
        print(_yellow(f"  ⚠  Found existing .env at {env_path}"))
        answer = _ask("     Overwrite it? [y/N] ", default="n")
        if answer.lower() != "y":
            print("  Keeping existing .env — init cancelled.")
            print()
            return

    print(_bold("  Step 1/3 — LLM Provider"))
    print()
    print("  How should ResponseIQ reason about your incidents?")
    print()
    print("    [1] Ollama  (free, fully local — recommended for privacy)")
    print("    [2] OpenAI  (cloud, most accurate)")
    print("    [3] None    (rule-engine fallback — no reasoning, zero config)")
    print()
    choice = _ask("  Choice [1]: ", default="1")

    lines: list[str] = [
        "# ResponseIQ configuration — generated by `responseiq init`",
        "# Edit freely. Re-run `responseiq init` to regenerate.",
        "",
    ]

    llm_provider = "none"
    if choice == "2":
        llm_provider = "openai"
        key = _ask("  OpenAI API key (sk-...): ", default="")
        if key:
            lines.append(f"OPENAI_API_KEY={key}")
        model = _ask("  Model [gpt-4o]: ", default="gpt-4o")
        lines.append(f"LLM_ANALYSIS_MODEL={model}")
        lines.append("LLM_FAST_MODEL=gpt-4o-mini")
        lines.append("LLM_REPRO_MODEL=gpt-4o")
    elif choice == "3":
        llm_provider = "none"
        lines.append("# No LLM configured — rule-engine fallback active")
    else:  # default = ollama
        llm_provider = "ollama"
        host = _ask("  Ollama base URL [http://localhost:11434/v1]: ", default="http://localhost:11434/v1")
        model = _ask("  Model [llama3.2]: ", default="llama3.2")
        lines.append(f"LLM_BASE_URL={host}")
        lines.append(f"LLM_ANALYSIS_MODEL={model}")
        lines.append(f"LLM_FAST_MODEL={model}")
        lines.append(f"LLM_REPRO_MODEL={model}")

    print()
    print(_bold("  Step 2/3 — Trust Policy"))
    print()
    print("  How much autonomy should ResponseIQ have?")
    print()
    print("    [1] suggest_only   — Print recommendations, never touch files  (safest)")
    print("    [2] pr_only        — Create GitHub PRs, never apply directly    (recommended)")
    print("    [3] guarded_apply  — Apply fixes locally after Trust Gate pass  (advanced)")
    print()
    policy_choice = _ask("  Policy [2]: ", default="2")
    policy_map = {"1": "suggest_only", "2": "pr_only", "3": "guarded_apply"}
    policy = policy_map.get(policy_choice, "pr_only")

    lines.append("")
    lines.append(f"RESPONSEIQ_POLICY_MODE={policy}")
    lines.append("RESPONSEIQ_SCRUB_ENABLED=true")
    lines.append("RESPONSEIQ_LOG_LEVEL=WARNING")

    print()
    print(_bold("  Step 3/3 — GitHub Integration  (optional)"))
    print()
    github_token = _ask("  GitHub token (leave blank to skip): ", default="")
    if github_token:
        lines.append("")
        lines.append(f"RESPONSEIQ_GITHUB_TOKEN={github_token}")

    lines.append("")

    env_path.write_text("\n".join(lines) + "\n", encoding="utf-8")

    print()
    print(_dim(f"  {sep}"))
    print(_green(f"  ✔  Wrote {env_path}"))
    print()

    # ── smoke test ──────────────────────────────────────────────────────────
    samples_log = _find_project_root() / "samples" / "crash.log"
    if samples_log.exists() and llm_provider != "none":
        print(_dim("  Running a quick smoke test against samples/crash.log …"))
        import subprocess

        result = subprocess.run(
            [sys.executable, "-m", "responseiq.cli", "--mode", "scan", "--target", str(samples_log)],
            capture_output=True,
            text=True,
            timeout=60,
        )
        if result.returncode == 0:
            print(_green("  ✔  Smoke test passed — ResponseIQ is ready."))
        else:
            print(_yellow("  ⚠  Smoke test returned non-zero (LLM may not be reachable yet)."))
        print()

    print(_bold("  You're set up. Try it now:"))
    print()
    if samples_log.exists():
        print(_cyan("    responseiq --mode scan --target ./samples/crash.log"))
        print(_cyan("    responseiq --mode fix  --target ./samples/crash.log"))
    else:
        print(_cyan("    responseiq --mode scan --target ./logs/"))
    print()
    print(_dim("  Full docs: https://github.com/infoyouth/responseiq"))
    print()


def _ask(prompt: str, default: str) -> str:
    """Print a prompt and read stdin; return ``default`` if the user hits Enter."""
    try:
        raw = input(prompt).strip()
        return raw if raw else default
    except (EOFError, KeyboardInterrupt):
        print()
        return default


def _find_project_root():
    """Walk up from cwd looking for pyproject.toml / .git; fall back to cwd."""
    from pathlib import Path

    here = Path.cwd()
    for parent in [here, *here.parents]:
        if (parent / "pyproject.toml").exists() or (parent / ".git").exists():
            return parent
    return here


def main():
    # ── init is a top-level command, not a plugin ──────────────────────────
    if len(sys.argv) >= 2 and sys.argv[1] == "init":
        _run_init()
        sys.exit(0)

    parser = argparse.ArgumentParser(
        description=(
            "ResponseIQ CLI — AI-native self-healing infrastructure copilot.\n\n"
            "Special commands (run before flags are parsed):\n"
            "  responseiq init   Interactive setup wizard (writes .env)"
        ),
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
    parser.add_argument("--target", default=None, help="Directory to scan (defaults to './logs' if exists)")
    parser.add_argument("--mode", default="scan", choices=["scan", "fix", "shadow"], help="Operation mode")
    parser.add_argument("--debug", action="store_true", help="Enable debug logging (shortcut for --log-level DEBUG)")
    parser.add_argument(
        "--log-level",
        default=None,
        choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
        help="Set log level (default: WARNING; use --debug for DEBUG)",
    )

    # P2.1 Shadow Analytics Arguments
    parser.add_argument("--shadow-mode", action="store_true", help="Run shadow analysis without applying fixes")
    parser.add_argument("--shadow-report", action="store_true", help="Generate management value report")
    parser.add_argument("--shadow-days", type=int, default=7, help="Days to include in shadow report (default: 7)")
    parser.add_argument(
        "--shadow-format",
        choices=["json", "markdown", "executive"],
        default="markdown",
        help="Shadow report output format",
    )

    # Support for GitHub Action inputs
    parser.add_argument("--action", choices=["scan", "fix", "shadow"], help="Alias for --mode")
    parser.add_argument("--url", help="Repository URL (e.g., https://github.com/owner/repo)")
    parser.add_argument("--token", help="GitHub Token")

    # Explainability — emit REASONING.md alongside fix output
    parser.add_argument(
        "--explain",
        action="store_true",
        help="Write a REASONING.md audit log alongside fix output (explainability)",
    )

    # Ignored args that might be passed by action.yml but handled via ENV
    parser.add_argument("--github-token", help=argparse.SUPPRESS)
    parser.add_argument("--openai-api-key", help=argparse.SUPPRESS)

    args, unknown = parser.parse_known_args()

    # Determine log level: --debug > --log-level > default WARNING
    log_level = "DEBUG" if args.debug else (args.log_level or "WARNING")
    import logging

    logging.basicConfig(
        stream=sys.stderr,
        level=getattr(logging, log_level),
        format="%(asctime)s | %(levelname)s | %(message)s",
    )

    # Map Action inputs to CLI args
    mode = args.action if args.action else args.mode
    if args.shadow_mode or args.shadow_report:
        mode = "shadow"

    # Set Env vars from args if provided
    if args.token:
        os.environ["GITHUB_TOKEN"] = args.token
    if args.url:
        if "github.com/" in args.url:
            repo_slug = args.url.split("github.com/")[-1].replace(".git", "")
            os.environ["GITHUB_REPOSITORY"] = repo_slug

    # Initialize AgentState with global context and trace_id
    trace_id = os.environ.get("TRACEPARENT") or os.environ.get("TRACE_ID")
    agent_state: AgentState = {
        "context": {
            "args": vars(args),
            "env": dict(os.environ),
        },
        "trace_id": trace_id,
    }

    # Plugin loading and execution
    registry = PluginRegistry()
    if mode not in registry.plugins:
        print(f"Unknown command: {mode}", file=sys.stderr)
        sys.exit(1)
    plugin_cls = registry.get_plugin(mode)
    plugin = plugin_cls()
    try:
        updated_state = plugin.run(agent_state)
        _print_result(mode, updated_state, explain=getattr(args, "explain", False))
        sys.exit(0)
    except Exception as e:
        telemetry.emit_event("PluginError", {"plugin": mode, "error": str(e)})
        print(f"Error running plugin {mode}: {e}", file=sys.stderr)
        sys.exit(1)


def _write_reasoning_md(fixes: list, target: str) -> None:
    """Write a REASONING.md audit log explaining every Trust Gate decision."""
    import datetime
    from pathlib import Path

    lines = [
        "# ResponseIQ — Reasoning Audit Log",
        "",
        f"> Generated: {datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}  ",
        f"> Target: `{target}`",
        "",
        "---",
        "",
        "## Decision Trace",
        "",
        "This document captures the full reasoning chain for every remediation",
        "proposed by ResponseIQ. It is intended to be committed alongside the",
        "patch so that reviewers can verify *why* the change was made.",
        "",
    ]

    for i, fix in enumerate(fixes, 1):
        title = fix.get("title") or "Untitled"
        severity = (fix.get("severity") or "unknown").upper()
        allowed = fix.get("allowed", False)
        confidence = fix.get("confidence", 0)
        impact = fix.get("impact_score", 0)
        blast = fix.get("blast_radius", "unknown")
        mode_used = fix.get("execution_mode") or "suggest_only"
        rationale = fix.get("rationale") or "_Not provided by LLM._"
        plan = fix.get("remediation_plan") or "_Not provided._"
        rollback = fix.get("rollback_plan") or "_Not provided._"
        test_plan = fix.get("test_plan") or "_Not provided._"
        passed = fix.get("checks_passed") or []
        failed = fix.get("checks_failed") or []
        model = fix.get("llm_model_used") or "_unknown_"
        suspect_commit = fix.get("suspect_commit")
        causal_graph = fix.get("causal_graph")

        verdict = "✅ APPROVED" if allowed else "🚫 BLOCKED"
        lines += [
            f"### {i}. {title}",
            "",
            "| Field | Value |",
            "|---|---|",
            f"| Severity | `{severity}` |",
            f"| Trust Gate | {verdict} |",
            f"| Execution Mode | `{mode_used}` |",
            f"| Confidence | {confidence:.0%} |",
            f"| Impact Score | {impact:.1f}/100 |",
            f"| Blast Radius | `{blast}` |",
            f"| LLM Model | `{model}` |",
            "",
            "#### Why this fix?",
            "",
            rationale,
            "",
            "#### Remediation Plan",
            "",
            plan,
            "",
            "#### Rollback Plan",
            "",
            rollback,
            "",
            "#### Test Plan",
            "",
            test_plan,
            "",
        ]

        if suspect_commit:
            lines += [
                "#### Suspect Commit (Git Correlation)",
                "",
                f"`{suspect_commit}`",
                "",
            ]

        if causal_graph:
            import json as _json

            lines += [
                "#### Causal Graph",
                "",
                "```json",
                _json.dumps(causal_graph, indent=2),
                "```",
                "",
            ]

        checks_block = []
        if passed:
            checks_block.append(f"**Passed:** {', '.join(f'`{c}`' for c in passed)}")
        if failed:
            checks_block.append(f"**Failed:** {', '.join(f'`{c}`' for c in failed)}")
        if checks_block:
            lines += ["#### Trust Gate Checks", ""] + checks_block + [""]

        lines.append("---")
        lines.append("")

    lines += [
        "## Disclaimer",
        "",
        "This audit log was generated by ResponseIQ. All fixes are AI-assisted",
        "and **require human review** before being applied to production.",
        "Confidence scores reflect LLM self-assessment, not external validation.",
        "",
    ]

    out_path = Path("REASONING.md")
    out_path.write_text("\n".join(lines), encoding="utf-8")
    print(f"  Audit log written → {out_path.resolve()}")


# ---------------------------------------------------------------------------
# Human-readable output formatter
# ---------------------------------------------------------------------------

_SEVERITY_ICON = {
    "critical": "[CRITICAL]",
    "high": "[HIGH]   ",
    "medium": "[MEDIUM] ",
    "low": "[LOW]    ",
    "info": "[INFO]   ",
}


def _print_result(mode: str, state: dict, explain: bool = False) -> None:
    """Render a clean, human-readable summary to stdout."""
    sep = "-" * 60

    if mode == "scan":
        result = state.get("scan_result", "unknown")
        error = state.get("scan_error")
        incidents = state.get("incidents", [])
        total = state.get("total_scanned", 0)
        target = state.get("context", {}).get("args", {}).get("target", "unknown")

        print(sep)
        print("  ResponseIQ Scan Report")
        print(f"  Target : {target}")
        print(f"  Status : {result.upper()}")
        print(sep)

        if error:
            print(f"  ERROR: {error}")
            print(sep)
            return

        if not incidents:
            print("  No incidents detected.")
            print(sep)
            return

        print(f"  Scanned  : {total} message(s)")
        print(f"  Incidents: {len(incidents)} found")
        print(sep)

        for i, inc in enumerate(incidents, 1):
            severity = (inc.get("severity") or "unknown").lower()
            icon = _SEVERITY_ICON.get(severity, f"[{severity.upper()[:6]}]")
            title = inc.get("title") or "Untitled Incident"
            description = inc.get("description") or ""
            source = inc.get("source") or "unknown"
            impact = inc.get("impact_score")

            print(f"  {i}. {icon} {title}")
            print(f"     Source     : {source}")
            if impact is not None:
                print(f"     Impact     : {impact:.1f}/100")
            if description:
                # Wrap long descriptions
                words = description.split()
                line: list[str] = []
                lines: list[str] = []
                for w in words:
                    if sum(len(x) + 1 for x in line) + len(w) > 70:
                        lines.append(" ".join(line))
                        line = [w]
                    else:
                        line.append(w)
                if line:
                    lines.append(" ".join(line))
                print(f"     Description: {lines[0]}")
                for line_part in lines[1:]:
                    print(f"                  {line_part}")
            remediation = inc.get("remediation")
            if remediation:
                print(f"     Fix        : {remediation[:120]}")
            print()

        print(sep)
        print("  Tip: run with --mode fix to apply safe remediations.")
        print(sep)

    elif mode == "fix":
        result = state.get("fix_result", "unknown")
        error = state.get("fix_error")
        fixes: list = state.get("fixes", [])
        total = state.get("total_scanned", 0)
        target = state.get("context", {}).get("args", {}).get("target", "unknown")

        print(sep)
        print("  ResponseIQ Fix Report")
        print(f"  Target : {target}")
        print(f"  Status : {result.upper()}")
        print(sep)

        if error:
            print(f"  ERROR: {error}")
            print(sep)
            return

        if not fixes:
            print("  No actionable incidents found (severity < HIGH).")
            print(sep)
            return

        print(f"  Scanned  : {total} message(s)")
        print(f"  Fixes    : {len(fixes)} remediation(s) generated")
        print(sep)

        for i, fix in enumerate(fixes, 1):
            severity = (fix.get("severity") or "unknown").lower()
            icon = _SEVERITY_ICON.get(severity, f"[{severity.upper()[:6]}]")
            title = fix.get("title") or "Untitled"
            print(f"  {i}. {icon} {title}")
            print(f"     Allowed         : {'YES' if fix.get('allowed') else 'NO (suggest_only)'}")
            print(f"     Confidence      : {fix.get('confidence', 0):.0%}")
            print(f"     Impact Score    : {fix.get('impact_score', 0):.1f}/100")
            print(f"     Blast Radius    : {fix.get('blast_radius', 'unknown')}")
            print(f"     Execution Mode  : {fix.get('execution_mode') or 'suggest_only'}")
            rationale = fix.get("rationale") or ""
            if rationale:
                print(f"     Rationale       : {rationale[:120]}")
            plan = fix.get("remediation_plan") or ""
            if plan:
                print(f"     Remediation Plan: {plan[:120]}")
            rollback = fix.get("rollback_plan") or ""
            if rollback:
                print(f"     Rollback Plan   : {rollback[:120]}")
            test_plan = fix.get("test_plan") or ""
            if test_plan:
                print(f"     Test Plan       : {test_plan[:120]}")
            passed = fix.get("checks_passed") or []
            failed = fix.get("checks_failed") or []
            if passed:
                print(f"     Checks Passed   : {', '.join(passed[:4])}")
            if failed:
                print(f"     Checks Failed   : {', '.join(failed[:4])}")
            next_steps = fix.get("next_steps") or []
            for ns in next_steps[:3]:
                print(f"     Next Step       : {ns[:100]}")
            print()

        print(sep)
        print("  Trust Gate: set RESPONSEIQ_POLICY_MODE=apply to execute changes.")
        print(sep)

        if explain:
            _write_reasoning_md(fixes, target)

    else:
        # Shadow or unknown — print non-env keys
        print(sep)
        print(f"  ResponseIQ {mode.title()} Report")
        print(sep)
        for k, v in state.items():
            if k not in ("context", "trace_id") and v is not None:
                print(f"  {k}: {v}")
        print(sep)


if __name__ == "__main__":
    main()
