"""Reusable Rich UI helpers for the Sentra CLI."""

from __future__ import annotations

from contextlib import contextmanager
from pathlib import Path
from typing import Iterable

from rich.panel import Panel
from rich.table import Table
from rich.text import Text

from sentra.console import console
from sentra.models import CorrelationArtifact, FirmwareEvidence, NetworkScanArtifact, StatusArtifact, WorkspaceInspection


LONG_SCAN_HOST_THRESHOLD = 32


@contextmanager
def progress_message(message: str) -> Iterable[None]:
    """Show a spinner while a CLI step is running."""

    with console.status(message, spinner="dots"):
        yield


def build_network_scan_table(artifact: NetworkScanArtifact) -> Table:
    table = Table(title="Network Scan Results")
    table.add_column("Host")
    table.add_column("IP")
    table.add_column("Services")
    table.add_column("Protocols")
    table.add_column("Fingerprint")
    table.add_column("Findings", justify="right")

    for device in artifact.devices:
        services = ", ".join(f"{service.service}:{service.port}/{service.protocol}" for service in device.services) or "None"
        protocols = ", ".join(device.protocols) or "None"
        fingerprint = device.fingerprint_matches[0] if device.fingerprint_matches else "Unknown"
        table.add_row(device.host, device.ip_address, services, protocols, fingerprint, str(len(device.findings)))
    return table


def build_firmware_scan_table(artifact: FirmwareEvidence) -> Table:
    table = Table(title="Firmware Scan Results")
    table.add_column("Path")
    table.add_column("Extractor")
    table.add_column("Vendors")
    table.add_column("Signals", justify="right")
    table.add_column("Artifacts", justify="right")
    table.add_column("Findings", justify="right")
    table.add_row(
        artifact.path,
        artifact.extraction_metadata.provider or "none",
        ", ".join(artifact.suspected_vendors) or "Unknown",
        str(
            len(artifact.config_hints)
            + len(artifact.service_hints)
            + len(artifact.software_components)
            + len(artifact.certificate_findings)
        ),
        str(len(artifact.extracted_artifacts)),
        str(len(artifact.findings)),
    )
    return table


def build_correlation_table(artifact: CorrelationArtifact) -> Table:
    table = Table(title="Evidence Graph Summary")
    table.add_column("Metric")
    table.add_column("Value", justify="right")
    for key, value in artifact.summary.items():
        table.add_row(key.replace("_", " ").title(), str(value))
    top_path = artifact.insights.get("top_weak_path", {}) if artifact.insights else {}
    if isinstance(top_path, dict) and top_path.get("path"):
        table.add_row("Top Risky Path", str(top_path["path"]))
        table.add_row("Top Path Score", str(top_path.get("score", "n/a")))
    return table


def build_status_table(artifact: StatusArtifact) -> Table:
    table = Table(title="Workspace Status")
    table.add_column("Area")
    table.add_column("Metric")
    table.add_column("Value", justify="right")
    table.add_row("Workspace", "Workspace", artifact.workspace)
    table.add_row("Workspace", "Version", artifact.version)
    table.add_row("Artifacts", "Artifacts", str(artifact.artifact_count))
    table.add_row("Assets", "Devices", str(artifact.device_count))
    table.add_row("Assets", "Findings", str(artifact.finding_count))
    table.add_row("Graph", "Graph Nodes", str(artifact.graph_nodes))
    table.add_row("Graph", "Graph Edges", str(artifact.graph_edges))
    table.add_row("Firmware", "Extracted Files", str(artifact.firmware_extracted_files))
    table.add_row("Firmware", "Firmware Certs", str(artifact.firmware_certificates))
    table.add_row("Firmware", "Cert Findings", str(artifact.firmware_certificate_findings))
    table.add_row("Firmware", "Config Hints", str(artifact.firmware_config_hints))
    table.add_row("Firmware", "Service Hints", str(artifact.firmware_service_hints))
    table.add_row("Firmware", "Firmware Components", str(artifact.firmware_components))
    table.add_row("Firmware", "Inference Hints", str(artifact.firmware_inference_hints))
    table.add_row("Firmware", "Extractor", artifact.firmware_extraction_provider or "none")
    return table


def build_workspace_inspection_table(inspection: WorkspaceInspection) -> Table:
    table = Table(title="Workspace Artifacts")
    table.add_column("Artifact")
    table.add_column("Present")
    table.add_column("Summary")
    table.add_column("Size", justify="right")
    table.add_column("Path")
    for artifact in inspection.artifacts:
        table.add_row(
            artifact.name,
            "yes" if artifact.present else "no",
            "available" if artifact.present else "missing",
            str(artifact.size_bytes),
            artifact.path,
        )
    return table


def build_summary_panel(command_name: str, rows: dict[str, object]) -> Panel:
    body = "\n".join(f"[bold]{label}:[/bold] {value}" for label, value in rows.items()) or "No summary available."
    return Panel(body, title=f"{command_name} Summary", border_style="green")


def build_startup_banner(title: str, subtitle: str) -> Panel:
    return Panel(
        f"[bold cyan]{title}[/bold cyan]\n[dim]{subtitle}[/dim]",
        border_style="cyan",
        title="Sentra",
    )


def build_root_overview(version: str) -> Panel:
    body = "\n".join(
        [
            f"[bold]Version:[/bold] {version}",
            "[bold]What Sentra does:[/bold] Defensive IoT discovery, firmware inspection, evidence correlation, and operator-ready reporting",
            "[bold]Built for:[/bold] Authorized security testing, asset visibility, and lab research",
            "[bold]Run first:[/bold] `sentra quick-scan --target 192.168.1.0/24 --yes`",
            "[bold]Common workflow:[/bold] `sentra scan net` -> `sentra correlate` -> `sentra report --format html`",
            "[bold]Useful follow-up:[/bold] `sentra status` and `sentra inspect-workspace`",
            "[bold]Outputs:[/bold] terminal, markdown, json, html",
        ]
    )
    return Panel(body, title="Sentra Overview", subtitle="Guided starting points", border_style="cyan")


def build_error_panel(message: str, title: str = "Sentra Error") -> Panel:
    text = Text(message)
    return Panel(text, title=title, border_style="red")


def build_confirmation_message(target: str, host_count: int) -> str:
    return f"Target {target} expands to {host_count} hosts. Continue with the scan?"


def default_scan_summary(artifact: NetworkScanArtifact, output: Path) -> Panel:
    return build_summary_panel(
        "Network Scan",
        {
            "Target": artifact.target,
            "Devices": len(artifact.devices),
            "Artifacts": output,
        },
    )


def default_firmware_summary(artifact: FirmwareEvidence, output: Path) -> Panel:
    return build_summary_panel(
        "Firmware Scan",
        {
            "Image": artifact.path,
            "Extracted Files": len(artifact.extracted_artifacts),
            "Components": len(artifact.software_components),
            "Findings": len(artifact.findings),
            "Artifacts": output,
        },
    )


def default_correlation_summary(artifact: CorrelationArtifact, output: Path) -> Panel:
    return build_summary_panel(
        "Correlation",
        {
            "Nodes": artifact.summary.get("nodes", 0),
            "Edges": artifact.summary.get("edges", 0),
            "Artifacts": output,
        },
    )


def default_report_summary(output_format: str, artifact_count: int, destination: Path | None = None) -> Panel:
    rows: dict[str, object] = {
        "Format": output_format,
        "Artifacts": artifact_count,
    }
    if destination is not None:
        rows["Report Path"] = destination
    return build_summary_panel("Report", rows)


def default_quick_scan_summary(
    target: str,
    workspace: Path,
    network_artifact: Path,
    correlation_artifact: Path,
    report_output: Path | None,
    firmware_artifact: Path | None = None,
) -> Panel:
    rows: dict[str, object] = {
        "Target": target,
        "Workspace": workspace,
        "Network Artifact": network_artifact,
        "Correlation Artifact": correlation_artifact,
    }
    if firmware_artifact is not None:
        rows["Firmware Artifact"] = firmware_artifact
    if report_output is not None:
        rows["Report Output"] = report_output
    return build_summary_panel("Quick Scan", rows)


def default_status_summary(artifact: StatusArtifact) -> Panel:
    return build_summary_panel(
        "Status",
        {
            "Workspace": artifact.workspace,
            "Capabilities": len(artifact.capabilities),
            "Artifacts": artifact.artifact_count,
            "Devices": artifact.device_count,
            "Firmware Signals": artifact.firmware_config_hints + artifact.firmware_service_hints + artifact.firmware_components,
        },
    )


def default_workspace_inspection_summary(inspection: WorkspaceInspection) -> Panel:
    return build_summary_panel(
        "Workspace Inspection",
        {
            "Workspace": inspection.workspace,
            "State Directory": inspection.state_dir,
            "Artifacts Present": inspection.artifact_count,
            "Extract Dirs": inspection.firmware_extract_dirs,
            "Extract Files": inspection.firmware_extract_files,
            "Report Artifacts": sum(1 for item in inspection.artifacts if item.present and item.name.startswith("report")),
        },
    )


def network_scan_markdown(artifact: NetworkScanArtifact, output: Path) -> str:
    lines = ["# Network Scan Results", "", f"- target: {artifact.target}", f"- artifact: {output}", ""]
    if artifact.devices:
        lines.extend(["| Host | IP | Services | Protocols | Findings |", "| --- | --- | --- | --- | --- |"])
        for device in artifact.devices:
            services = ", ".join(f"{service.service}:{service.port}/{service.protocol}" for service in device.services) or "None"
            protocols = ", ".join(device.protocols) or "None"
            lines.append(f"| {device.host} | {device.ip_address} | {services} | {protocols} | {len(device.findings)} |")
    else:
        lines.append("No devices discovered.")
    return "\n".join(lines) + "\n"


def firmware_scan_markdown(artifact: FirmwareEvidence, output: Path) -> str:
    vendors = ", ".join(artifact.suspected_vendors) or "Unknown"
    lines = [
        "# Firmware Scan Results",
        "",
        f"- path: {artifact.path}",
        f"- artifact: {output}",
        f"- sha256: {artifact.sha256}",
        f"- vendors: {vendors}",
        f"- extracted_files: {len(artifact.extracted_artifacts)}",
        f"- software_components: {len(artifact.software_components)}",
        f"- findings: {len(artifact.findings)}",
        "",
    ]
    if artifact.findings:
        lines.append("| Severity | Title |")
        lines.append("| --- | --- |")
        for finding in artifact.findings:
            lines.append(f"| {finding.severity} | {finding.title} |")
    return "\n".join(lines) + "\n"


def correlation_markdown(artifact: CorrelationArtifact, output: Path) -> str:
    lines = ["# Evidence Graph Summary", "", f"- artifact: {output}", ""]
    for key, value in artifact.summary.items():
        lines.append(f"- {key}: {value}")
    return "\n".join(lines) + "\n"

