"""Modern minimal Rich terminal UI for Oncecheck."""

from __future__ import annotations

import time
from pathlib import Path
from typing import List, Optional, Tuple

import readchar
from rich.console import Console
from rich.live import Live
from rich.panel import Panel
from rich.table import Table

from .. import __version__
from ..engine.runner import Finding, ScanResult

console = Console()

_ACCENT = "#eeef20"
_SUCCESS = "#22c55e"
_WARN = "#eab308"
_DANGER = "#ef4444"
_MUTED = "#a1a1aa"

_SEVERITY_STYLES = {
    "FAIL": f"bold {_DANGER}",
    "WARN": f"bold {_WARN}",
    "INFO": "bold #60a5fa",
}

_SEVERITY_ICONS = {
    "FAIL": "x",
    "WARN": "!",
    "INFO": "i",
}

_STEP_STYLES = {
    "pending": ("o", _MUTED),
    "running": ("~", _ACCENT),
    "done": ("+", _SUCCESS),
    "warn": ("!", _WARN),
    "failed": ("x", _DANGER),
}


def arrow_select(
    title: str,
    options: List[Tuple[str, str]],
    *,
    icons: Optional[List[str]] = None,
) -> str:
    """Render an arrow-key menu and return selected value."""
    selected = 0
    total = len(options)

    while True:
        lines: List[str] = []
        for i, (label, _value) in enumerate(options):
            icon = icons[i] if icons else ""
            if i == selected:
                prefix = f"[bold {_ACCENT}]>[/bold {_ACCENT}]"
                text = f"[bold]{label}[/bold]"
            else:
                prefix = " "
                text = f"[dim]{label}[/dim]"
            if icon:
                lines.append(f"  {prefix} {icon} {text}")
            else:
                lines.append(f"  {prefix} {text}")

        panel = Panel(
            "\n".join(lines) + "\n\n[dim]Use up/down + Enter[/dim]",
            title=f"[bold]{title}[/bold]",
            border_style=_ACCENT,
            expand=False,
            padding=(1, 2),
        )
        console.clear()
        console.print(panel)
        key = readchar.readkey()
        if key == readchar.key.UP:
            selected = (selected - 1) % total
        elif key == readchar.key.DOWN:
            selected = (selected + 1) % total
        elif key in (readchar.key.ENTER, "\r", "\n"):
            console.clear()
            return options[selected][1]
        elif key in ("q", readchar.key.ESC):
            console.clear()
            return options[-1][1]


def print_welcome() -> None:
    """Show a minimal welcome screen."""
    console.clear()
    panel = Panel(
        f"[bold]Oncecheck[/bold] v{__version__}\n"
        "[dim]Scan iOS, Android, and Web projects for compliance risks.[/dim]",
        border_style=_ACCENT,
        expand=False,
        padding=(1, 2),
    )
    console.print(panel)
    console.print("  [dim]Press any key to continue...[/dim]")
    readchar.readkey()


def prompt_main_menu() -> str:
    return arrow_select(
        "Main Menu",
        [
            ("Scan project", "scan"),
            ("Tools", "tools"),
            ("Login", "login"),
            ("Logout", "logout"),
            ("Subscription status", "status"),
            ("Exit", "exit"),
        ],
    )


def prompt_platform() -> str:
    return arrow_select(
        "Select Platform",
        [
            ("Auto-detect", "auto"),
            ("iOS", "ios"),
            ("Android", "android"),
            ("Web", "web"),
        ],
    )


def prompt_analysis_mode() -> str:
    return arrow_select(
        "Analysis Mode",
        [
            ("Hybrid (recommended)", "hybrid"),
            ("Advanced engines only", "advanced"),
            ("Heuristic only", "heuristic"),
        ],
    )


def prompt_require_compiler_engine() -> bool:
    choice = arrow_select(
        "Compiler Engine Requirement",
        [
            ("Best effort (fallback allowed)", "best_effort"),
            ("Strict (fail if unavailable)", "strict"),
        ],
    )
    return choice == "strict"


def prompt_advanced_profile() -> str:
    return arrow_select(
        "Advanced Engine Profile",
        [
            ("Balanced", "balanced"),
            ("CodeQL first", "codeql-first"),
            ("CodeQL only", "codeql-only"),
            ("Semgrep only", "semgrep-only"),
        ],
    )


def prompt_project_path() -> str:
    console.print()
    console.print(
        Panel(
            "[bold]Project path[/bold]\n[dim]Press Enter to use current directory.[/dim]",
            border_style=_ACCENT,
            expand=False,
            padding=(1, 2),
        )
    )
    while True:
        path_str = console.input(f"  [bold {_ACCENT}]>[/bold {_ACCENT}] ").strip()
        if not path_str:
            path_str = "."
        p = Path(path_str).expanduser().resolve()
        if p.is_dir():
            return str(p)
        console.print(f"  [bold {_DANGER}]Invalid directory:[/bold {_DANGER}] {path_str}")


def prompt_tools_menu() -> str:
    return arrow_select(
        "Tools",
        [
            ("Advanced engines", "engines"),
            ("Rules center", "rules"),
            ("Benchmark", "benchmark"),
            ("Suppressions", "suppressions"),
            ("Initialize config", "init"),
            ("Shell completions", "completions"),
            ("Back", "back"),
        ],
    )


def prompt_rules_menu() -> str:
    return arrow_select(
        "Rules Center",
        [
            ("Sync policy sources", "sync"),
            ("Policy freshness status", "status"),
            ("Policy impact (recent changes)", "impact"),
            ("List rules", "list"),
            ("Show rule details", "show"),
            ("Doctor check", "doctor"),
            ("Back", "back"),
        ],
    )


def prompt_rules_platform() -> str:
    return arrow_select(
        "Rule Platform Filter",
        [("All", "all"), ("iOS", "ios"), ("Android", "android"), ("Web", "web"), ("Common", "common")],
    )


def prompt_rules_severity() -> str:
    return arrow_select(
        "Rule Severity Filter",
        [("All", "ALL"), ("FAIL", "FAIL"), ("WARN", "WARN"), ("INFO", "INFO")],
    )


def prompt_rules_tier() -> str:
    return arrow_select(
        "Rule Tier Filter",
        [("All", "all"), ("Starter", "starter"), ("Team", "team")],
    )


def prompt_benchmark_menu() -> str:
    return arrow_select(
        "Benchmark",
        [
            ("Score predictions", "score"),
            ("Gate thresholds (CI)", "gate"),
            ("Run gate config", "gate-config"),
            ("Generate truth template", "template"),
            ("Back", "back"),
        ],
    )


def prompt_benchmark_suite() -> str:
    return arrow_select(
        "Benchmark Suite",
        [
            ("Custom", "custom"),
            ("OWASP Benchmark", "owasp-benchmark"),
            ("Juliet", "juliet"),
        ],
    )


def prompt_suppressions_menu() -> str:
    return arrow_select(
        "Suppressions",
        [
            ("Add suppression with reason", "add"),
            ("List suppressions", "list"),
            ("Back", "back"),
        ],
    )


def prompt_input(title: str, prompt: str, default: str = "") -> str:
    console.print()
    console.print(
        Panel(
            f"[bold]{title}[/bold]\n[dim]{prompt}[/dim]",
            border_style=_ACCENT,
            expand=False,
            padding=(1, 2),
        )
    )
    value = console.input(f"  [bold {_ACCENT}]>[/bold {_ACCENT}] ").strip()
    return value or default


def prompt_shell() -> str:
    return arrow_select(
        "Completions Shell",
        [("zsh", "zsh"), ("bash", "bash"), ("fish", "fish")],
    )


def prompt_confidence_threshold() -> float:
    value = prompt_input(
        "Minimum Confidence",
        "Only show findings >= this confidence (0.0-1.0). Press Enter for 0.0.",
        "0.0",
    )
    try:
        out = float(value)
    except ValueError:
        return 0.0
    return max(0.0, min(1.0, out))


def prompt_post_scan(is_team: bool = False) -> str:
    options = [("Browse findings", "browse")]
    if is_team:
        options.append(("Export results", "export"))
    options.extend([("Back to menu", "menu"), ("Exit", "exit")])
    return arrow_select("Next Action", options)


def prompt_export_format() -> str:
    return arrow_select("Export Format", [("JSON", "json"), ("Text", "text"), ("SARIF", "sarif")])


def prompt_export_location() -> str:
    home = Path.home()
    cwd = Path.cwd()
    options = [
        (f"Current directory ({cwd})", str(cwd)),
        (f"Desktop ({home / 'Desktop'})", str(home / "Desktop")),
        (f"Documents ({home / 'Documents'})", str(home / "Documents")),
        (f"Downloads ({home / 'Downloads'})", str(home / "Downloads")),
    ]
    valid = [(label, val) for label, val in options if Path(val).is_dir()]
    return arrow_select("Save Location", valid)


def print_step_status(
    index: int,
    total: int,
    label: str,
    state: str,
    detail: str = "",
) -> None:
    """Print one step-state line for the scan pipeline."""
    if state == "running":
        _animate_running_step(index, total, label, detail)
    icon, color = _STEP_STYLES.get(state, _STEP_STYLES["pending"])
    suffix = f" [dim]{detail}[/dim]" if detail else ""
    console.print(f"  [dim]{index}/{total}[/dim] [{color}]{icon}[/{color}] {label}{suffix}")


def _animate_running_step(index: int, total: int, label: str, detail: str = "") -> None:
    """Render a brief spinner animation for a running step."""
    frames = ["|", "/", "-", "\\"]
    suffix = f" {detail}" if detail else ""
    with Live(console=console, refresh_per_second=20, transient=True) as live:
        for frame in frames:
            live.update(
                f"  [dim]{index}/{total}[/dim] [bold {_ACCENT}]{frame}[/bold {_ACCENT}] {label}[dim]{suffix}[/dim]"
            )
            time.sleep(0.06)


def print_header() -> None:
    console.print(Panel("[bold]Scan Report[/bold]", border_style=_ACCENT, expand=False, padding=(0, 2)))


def print_scan_result(result: ScanResult) -> None:
    console.print()
    console.print(
        f"[bold]{result.platform.value.upper()}[/bold] "
        f"[dim]({result.project_type})[/dim] "
        f"[dim]- {result.scan_time_seconds:.1f}s[/dim]"
    )

    summary = Table(show_header=False, box=None, pad_edge=False)
    summary.add_row(f"[bold {_DANGER}]FAIL[/bold {_DANGER}]", str(result.fail_count))
    summary.add_row(f"[bold {_WARN}]WARN[/bold {_WARN}]", str(result.warn_count))
    summary.add_row("[bold #60a5fa]INFO[/bold #60a5fa]", str(result.info_count))
    console.print(Panel(summary, border_style=_ACCENT, expand=False, title="Summary"))

    if not result.findings:
        console.print(f"  [bold {_SUCCESS}]+ No compliance issues found[/bold {_SUCCESS}]")
        return

    severity_order = {"FAIL": 0, "WARN": 1, "INFO": 2}
    findings = sorted(result.findings, key=lambda f: severity_order.get(f.severity, 3))
    for finding in findings:
        _print_finding(finding)


def _print_finding(finding: Finding) -> None:
    style = _SEVERITY_STYLES.get(finding.severity, "bold")
    icon = _SEVERITY_ICONS.get(finding.severity, "?")
    console.print(f"  [{style}]{icon} {finding.severity}[/{style}] [bold]{finding.rule_id}[/bold] {finding.message}")
    console.print(f"      [dim]Fix: {finding.fix}[/dim]")
    if finding.reference:
        console.print(f"      [dim]Ref: {finding.reference}[/dim]")


def print_footer(results: List[ScanResult]) -> None:
    total_fail = sum(r.fail_count for r in results)
    total_warn = sum(r.warn_count for r in results)
    total_info = sum(r.info_count for r in results)
    console.print()
    console.print(
        f"[dim]Totals ->[/dim] "
        f"[bold {_DANGER}]FAIL {total_fail}[/bold {_DANGER}]  "
        f"[bold {_WARN}]WARN {total_warn}[/bold {_WARN}]  "
        f"[bold #60a5fa]INFO {total_info}[/bold #60a5fa]"
    )
