from dataclasses import dataclass

from rich import box
from rich.console import Console
from rich.progress import (
    BarColumn,
    Progress,
    TextColumn,
)
from rich.table import Table
from rich.text import Text

from vulnsearch.models.vulnerability import Package

console = Console()


@dataclass
class ScanSummary:
    """Scan counters for progress UI."""

    total: int
    vulnerable: int = 0


class ScanUI:
    """Progress UI for scanning multiple packages."""

    def __init__(self, total: int):
        self.summary = ScanSummary(total=total)

        self.progress = Progress(
            TextColumn("[bold]Scanning packages"),
            BarColumn(),
            TextColumn("{task.completed}/{task.total}"),
            TextColumn(" Vulnerable: {task.fields[vuln]}"),
            transient=True,
        )

        self.task_id = self.progress.add_task(
            "scan",
            total=total,
            vuln=0,
        )

    def __enter__(self):
        self.progress.start()
        return self

    def __exit__(self, *exc):
        self.progress.stop()
        console.print("[green]✔[/green] SBOM scan сompleted")

    def advance(self, is_vulnerable: bool) -> None:
        """Advance progress by one and update vulnerable counter."""
        if is_vulnerable:
            self.summary.vulnerable += 1

        self.progress.update(
            self.task_id,
            advance=1,
            vuln=self.summary.vulnerable,
        )


def _make_table(title: str) -> Table:
    """Create a consistent Rich table layout."""
    return Table(
        title=title,
        show_header=True,
        header_style="bold magenta",
        box=box.ASCII2,
        show_lines=True,
        expand=True,
    )


def print_vulns_table(pkgs: Package | list[Package]) -> None:
    """Print a Rich table of vulnerabilities for a single package."""
    if isinstance(pkgs, Package):
        pkgs = [pkgs]

    for pkg in pkgs:
        title = f"[bold]{pkg.name}[/bold]\n[dim]version: {pkg.version or '-'}\necosystem: {pkg.ecosystem or '-'}[/dim]"

        table = _make_table(title)
        table.add_column("ID / Aliases", no_wrap=False, ratio=2, min_width=18)
        table.add_column("Src", style="cyan", justify="center", no_wrap=True, width=6)
        table.add_column("Risk", justify="center", no_wrap=True, width=6)
        table.add_column("Summary", no_wrap=False, ratio=5, overflow="ellipsis")
        table.add_column("Links", no_wrap=False, ratio=3, min_width=20)

        for vuln in pkg.vulnerabilities:
            data = vuln.model_dump(exclude_none=True)

            id = Text(vuln.id, style="bold")
            if vuln.aliases:
                aliases = "\n".join(vuln.aliases)
                id.append("\n")
                id.append(aliases, style="dim")

            src = Text(vuln.source)

            risk = vuln.severity.severity.value
            color = vuln.severity.severity.to_rich_color()
            risk_text = f"{color}{risk}"

            summary = vuln.summary or "-"
            summary_text = Text(summary)

            references = "\n".join(data["references"])
            table.add_row(
                id,
                src,
                risk_text,
                summary_text,
                references,
            )

        console.print(table)


def _parse_cpe_prefix(cpe: str) -> tuple[str, str]:
    """Extract (product, vendor) from a CPE 2.3 string for display."""
    parts = cpe.split(":")

    vendor = parts[3] if len(parts) > 3 else "-"
    product = parts[4] if len(parts) > 4 else "-"

    if vendor in {"*", "-", ""}:
        vendor = "-"
    if product in {"*", "-", ""}:
        product = "-"

    return product, vendor


def print_cpe_table(cpes: dict[str, set[str]]) -> None:
    """Print grouped CPE matches as a Rich table."""
    table = _make_table(title="[bold]CPE Search Results[/bold]")

    table.add_column("Package", ratio=2, min_width=20)
    table.add_column("CPE", ratio=5)

    for cpe_prefix, full_cpes in cpes.items():
        product, vendor = _parse_cpe_prefix(cpe_prefix)

        package_text = Text(product)
        if vendor:
            package_text.append(f"\n({vendor})", style="dim")

        cpe_list = "\n".join(full_cpes)

        table.add_row(
            package_text,
            cpe_list,
        )

    console.print(table)
