import os
import subprocess
import sys

from rich.console import Console
from rich.prompt import Confirm, Prompt

try:
    import inquirer

    INTERACTIVE = True
except ImportError:
    INTERACTIVE = False

console = Console()


def run_cmd(cmd_list, error_msg="Git command failed"):
    try:
        result = subprocess.run(cmd_list, check=True, capture_output=True, text=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        console.print(f"[bold red]Error:[/bold red] {error_msg}\n[dim]{e.stderr}[/dim]")
        return None


def get_git_status():
    out = run_cmd(
        ["git", "status", "--porcelain"], "Could not get git status. Is this a repo?"
    )
    if not out:
        return []

    files = []
    for line in out.splitlines():
        if len(line) > 3:
            files.append(line[3:])
    return files


def run_push():
    console.print("[dim]Pushing to remote...[/dim]")
    try:
        subprocess.run(["git", "push"], check=True)
        console.print("[bold green] Deployment Complete. Code is live.[/bold green]")
    except subprocess.CalledProcessError:
        console.print(
            "[bold red] Push failed. Check your git remote settings.[/bold red]"
        )


def start_deployment_wizard():
    if not INTERACTIVE:
        console.print(
            "[yellow]Install 'inquirer' (pip install inquirer) to use interactive deployment.[/yellow]"
        )
        return

    console.print("\n[bold cyan] Skylos Deployment Wizard[/bold cyan]")

    files = get_git_status()
    if not files:
        console.print("[green]Working tree is clean.[/green]")
        if Confirm.ask("Push existing commits?"):
            run_push()
        return

    q_scope = [
        inquirer.List(
            "scope",
            message="What do you want to stage?",
            choices=[
                "All changed files",
                "Select files manually",
                "Skip commit (Push only)",
            ],
        ),
    ]
    ans_scope = inquirer.prompt(q_scope)
    if not ans_scope:
        return

    if ans_scope["scope"] == "Select files manually":
        q_files = [inquirer.Checkbox("files", message="Select files", choices=files)]
        ans_files = inquirer.prompt(q_files)
        if not ans_files or not ans_files["files"]:
            console.print("[red]No files selected.[/red]")
            return
        run_cmd(["git", "add"] + ans_files["files"])
        console.print(f"[green]Staged {len(ans_files['files'])} files.[/green]")

    elif ans_scope["scope"] == "All changed files":
        run_cmd(["git", "add", "."])
        console.print("[green]Staged all files.[/green]")

    if ans_scope["scope"] != "Skip commit (Push only)":
        msg = Prompt.ask("[bold green]Enter commit message[/bold green]")
        if not msg:
            console.print("[red]Commit message required.[/red]")
            return
        if run_cmd(["git", "commit", "-m", msg]):
            console.print("[green]✓ Committed.[/green]")

    if Confirm.ask("Ready to git push?"):
        run_push()


def _get_finding_file(finding):
    return finding.get("file", finding.get("file_path", ""))


def _check_agent_gate(findings_lists, agent_file_set, agent_cfg, reasons):
    """Evaluate agent-specific thresholds against findings in AI-authored files.

    Returns False if any agent threshold is exceeded.
    """
    agent_passed = True

    agent_danger = []
    agent_quality = []
    agent_secrets = []
    agent_dead_code = 0

    for category, items in findings_lists.items():
        for f in items:
            fpath = _get_finding_file(f)
            if fpath not in agent_file_set:
                continue
            if category == "danger":
                agent_danger.append(f)
            elif category == "quality":
                agent_quality.append(f)
            elif category == "secrets":
                agent_secrets.append(f)
            elif category == "dead_code":
                agent_dead_code += 1

    agent_critical = [
        d for d in agent_danger if str(d.get("severity", "")).lower() == "critical"
    ]
    agent_high = [
        d for d in agent_danger if str(d.get("severity", "")).lower() == "high"
    ]

    a_max_critical = agent_cfg.get("max_critical")
    if isinstance(a_max_critical, int) and len(agent_critical) > a_max_critical:
        agent_passed = False
        reasons.append(
            f"Agent gate: {len(agent_critical)} critical issue(s) in AI-authored files (max: {a_max_critical})"
        )

    a_max_high = agent_cfg.get("max_high")
    if isinstance(a_max_high, int) and len(agent_high) > a_max_high:
        agent_passed = False
        reasons.append(
            f"Agent gate: {len(agent_high)} high severity issue(s) in AI-authored files (max: {a_max_high})"
        )

    a_max_security = agent_cfg.get("max_security")
    if isinstance(a_max_security, int) and len(agent_danger) > a_max_security:
        agent_passed = False
        reasons.append(
            f"Agent gate: {len(agent_danger)} security issue(s) in AI-authored files (max: {a_max_security})"
        )

    a_max_quality = agent_cfg.get("max_quality")
    if isinstance(a_max_quality, int) and len(agent_quality) > a_max_quality:
        agent_passed = False
        reasons.append(
            f"Agent gate: {len(agent_quality)} quality issue(s) in AI-authored files (max: {a_max_quality})"
        )

    a_max_secrets = agent_cfg.get("max_secrets")
    if isinstance(a_max_secrets, int) and len(agent_secrets) > a_max_secrets:
        agent_passed = False
        reasons.append(
            f"Agent gate: {len(agent_secrets)} secret(s) in AI-authored files (max: {a_max_secrets})"
        )

    a_max_dead_code = agent_cfg.get("max_dead_code")
    if isinstance(a_max_dead_code, int) and agent_dead_code > a_max_dead_code:
        agent_passed = False
        reasons.append(
            f"Agent gate: {agent_dead_code} dead code issue(s) in AI-authored files (max: {a_max_dead_code})"
        )

    min_defend = agent_cfg.get("min_defend_score")
    require_defend = agent_cfg.get("require_defend", False)
    if require_defend or isinstance(min_defend, (int, float)):
        reasons.append(
            "Agent gate: require_defend/min_defend_score set but no defense data available (run skylos defend)"
        )
        agent_passed = False

    return agent_passed


def check_gate(results, config, strict=False, provenance=None):
    results = results or {}
    config = config or {}

    reasons = []
    passed = True

    total_findings = sum(
        len(results.get(k, []))
        for k in (
            "unused_functions",
            "unused_imports",
            "unused_variables",
            "unused_classes",
            "unused_parameters",
        )
    )

    danger = results.get("danger", []) or []
    quality = results.get("quality", []) or []
    secrets = results.get("secrets", []) or []

    critical_issues = []
    high_issues = []

    for issue in danger:
        sev = str(issue.get("severity", "")).lower()
        if sev == "critical":
            critical_issues.append(issue)
        elif sev == "high":
            high_issues.append(issue)

    gate_config = config.get("gate", {}) if config else {}

    if strict:
        total_issues = total_findings + len(danger) + len(quality) + len(secrets)
        if total_issues > 0:
            return False, [f"Strict mode: {total_issues} issue(s) found"]
        return True, []

    fail_on_critical = gate_config.get("fail_on_critical", True)
    max_critical = gate_config.get("max_critical", 0)
    max_high = gate_config.get("max_high", 5)
    max_security = gate_config.get("max_security", 10)
    max_quality = gate_config.get("max_quality", 10)
    max_secrets = gate_config.get("max_secrets", None)
    max_dead_code = gate_config.get("max_dead_code", None)

    if fail_on_critical and len(critical_issues) > 0:
        passed = False
        reasons.append(f"{len(critical_issues)} critical security issue(s)")

    elif isinstance(max_critical, int) and len(critical_issues) > max_critical:
        passed = False
        reasons.append(f"{len(critical_issues)} critical issues (max: {max_critical})")

    if isinstance(max_high, int) and len(high_issues) > max_high:
        passed = False
        reasons.append(f"{len(high_issues)} high severity issues (max: {max_high})")

    if isinstance(max_security, int) and len(danger) > max_security:
        passed = False
        reasons.append(f"{len(danger)} total security issues (max: {max_security})")

    if isinstance(max_quality, int) and len(quality) > max_quality:
        passed = False
        reasons.append(f"{len(quality)} quality issues (max: {max_quality})")

    if isinstance(max_secrets, int) and len(secrets) > max_secrets:
        passed = False
        reasons.append(f"{len(secrets)} secrets issues (max: {max_secrets})")

    if isinstance(max_dead_code, int) and total_findings > max_dead_code:
        passed = False
        reasons.append(f"{total_findings} dead code issue(s) (max: {max_dead_code})")

    # Agent-aware gating: apply stricter thresholds to AI-authored files
    agent_cfg = gate_config.get("agent")
    if provenance and agent_cfg and provenance.agent_files:
        agent_file_set = set(provenance.agent_files)

        dead_code_items = []
        for k in (
            "unused_functions",
            "unused_imports",
            "unused_variables",
            "unused_classes",
            "unused_parameters",
        ):
            dead_code_items.extend(results.get(k, []) or [])

        findings_lists = {
            "danger": danger,
            "quality": quality,
            "secrets": secrets,
            "dead_code": dead_code_items,
        }

        agent_passed = _check_agent_gate(
            findings_lists, agent_file_set, agent_cfg, reasons
        )
        if not agent_passed:
            passed = False

    return passed, reasons


def build_summary_markdown(results, passed, reasons):
    results = results or {}

    danger = results.get("danger", []) or []
    quality = results.get("quality", []) or []
    secrets = results.get("secrets", []) or []

    critical_count = sum(
        1 for d in danger if str(d.get("severity", "")).lower() == "critical"
    )
    high_count = sum(1 for d in danger if str(d.get("severity", "")).lower() == "high")
    dead_code_count = sum(
        len(results.get(k, []) or [])
        for k in (
            "unused_functions",
            "unused_imports",
            "unused_classes",
            "unused_variables",
            "unused_parameters",
        )
    )

    status = "PASSED" if passed else "FAILED"
    icon = "✅" if passed else "❌"

    lines = [
        "## Skylos Analysis Results",
        "",
        "| Category | Count | Status |",
        "|----------|-------|--------|",
        f"| Security (critical) | {critical_count} | {'✅' if critical_count == 0 else '❌'} |",
        f"| Security (high) | {high_count} | {'✅' if high_count <= 5 else '⚠️'} |",
        f"| Security (total) | {len(danger)} | {'✅' if len(danger) <= 10 else '⚠️'} |",
        f"| Quality | {len(quality)} | {'✅' if len(quality) <= 10 else '⚠️'} |",
        f"| Secrets | {len(secrets)} | {'✅' if len(secrets) == 0 else '❌'} |",
        f"| Dead Code | {dead_code_count} | ℹ️ |",
        "",
        f"**Result: {icon} {status}**",
    ]

    if reasons:
        lines.append("")
        lines.append("### Failure Reasons")
        for r in reasons:
            lines.append(f"- {r}")

    return "\n".join(lines)


def write_github_summary(markdown):
    summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
    if summary_path:
        try:
            with open(summary_path, "a") as f:
                f.write(markdown + "\n")
        except OSError as e:
            console.print(
                f"[yellow]Could not write to GITHUB_STEP_SUMMARY: {e}[/yellow]"
            )
    else:
        console.print(markdown)


def run_gate_interaction(
    *,
    results=None,
    result=None,
    config=None,
    strict=False,
    force=False,
    command_to_run=None,
    summary=False,
    provenance=None,
):
    console = Console()

    if results is None:
        results = result or {}

    config = config or {}
    gate_cfg = config.get("gate") or {}

    strict = bool(strict or gate_cfg.get("strict", False))

    try:
        passed, reasons = check_gate(
            results, config, strict=strict, provenance=provenance
        )
    except TypeError:
        passed, reasons = check_gate(results, config)

    if summary:
        md = build_summary_markdown(results, passed, reasons)
        write_github_summary(md)

    if passed:
        console.print("\n[bold green]✅ Quality Gate: PASSED[/bold green]")

        if command_to_run:
            proc = subprocess.run(command_to_run)
            return getattr(proc, "returncode", 0)

        return 0

    console.print("\n[bold red] Quality Gate: FAILED[/bold red]")
    for r in reasons or []:
        console.print(f"   • {r}")

    if force:
        console.print("[yellow] Forced pass (local only)[/yellow]")
        return 0

    if strict:
        return 1

    try:
        if sys.stdout.isatty():
            if Confirm.ask("Quality gate failed. Continue anyway?"):
                start_deployment_wizard()
                return 0
            return 1
    except Exception:
        pass

    return 1
