"""Workspace inspection and status helpers."""

from __future__ import annotations

from datetime import UTC, datetime
from pathlib import Path

from sentra import __version__
from sentra.models import (
    CapabilityRecord,
    CorrelationArtifact,
    FirmwareEvidence,
    NetworkScanArtifact,
    PassiveScanArtifact,
    StatusArtifact,
    WorkspaceArtifactRecord,
    WorkspaceInspection,
)
from sentra.state import load_json, normalize_path

_ARTIFACT_FILES: tuple[tuple[str, str], ...] = (
    ("network_scan", "network_scan.json"),
    ("passive_scan", "passive_scan.json"),
    ("firmware_scan", "firmware_scan.json"),
    ("correlation", "correlation.json"),
    ("report_json", "report.json"),
    ("report_markdown", "report.md"),
    ("report_html", "report.html"),
    ("scan_cache", "scan_cache.json"),
    ("scan_state", "scan_state.json"),
    ("fingerprint_overrides", "fingerprint_overrides.json"),
    ("cve_feed", "cve_feed.json"),
    ("firmware_extract_root", "../.sentra-fw"),
)


def inspect_workspace(workspace: Path) -> WorkspaceInspection:
    normalized_workspace = _normalize_workspace_path(workspace)
    state_dir = normalized_workspace / ".sentra"
    extract_root = normalized_workspace / ".sentra-fw"
    artifacts: list[WorkspaceArtifactRecord] = []
    for name, filename in _ARTIFACT_FILES:
        path = state_dir / filename
        if name == "firmware_extract_root":
            path = extract_root
        present = path.exists()
        stats = path.stat() if present else None
        size_bytes = 0
        if present and path.is_dir():
            size_bytes = sum(item.stat().st_size for item in path.rglob("*") if item.is_file())
        elif stats:
            size_bytes = int(stats.st_size)
        artifacts.append(
            WorkspaceArtifactRecord(
                name=name,
                path=str(path),
                present=present,
                size_bytes=size_bytes,
                modified_at=datetime.fromtimestamp(stats.st_mtime, tz=UTC) if stats else None,
            )
        )
    artifacts.sort(key=lambda item: item.name)
    firmware_extract_dirs = sum(1 for item in extract_root.iterdir() if item.is_dir()) if extract_root.exists() else 0
    firmware_extract_files = sum(1 for item in extract_root.rglob("*") if item.is_file()) if extract_root.exists() else 0
    return WorkspaceInspection(
        workspace=str(normalized_workspace),
        state_dir=str(state_dir),
        artifact_count=sum(1 for item in artifacts if item.present),
        firmware_extract_dirs=firmware_extract_dirs,
        firmware_extract_files=firmware_extract_files,
        artifacts=artifacts,
    )


def build_status_artifact(workspace: Path) -> StatusArtifact:
    inspection = inspect_workspace(workspace)
    normalized_workspace = Path(inspection.workspace)
    network_scan = _load_model(normalized_workspace / ".sentra" / "network_scan.json", NetworkScanArtifact)
    passive_scan = _load_model(normalized_workspace / ".sentra" / "passive_scan.json", PassiveScanArtifact)
    firmware_scan = _load_model(normalized_workspace / ".sentra" / "firmware_scan.json", FirmwareEvidence)
    correlation = _load_model(normalized_workspace / ".sentra" / "correlation.json", CorrelationArtifact)

    device_count = len(network_scan.devices) if network_scan else 0
    finding_count = 0
    if network_scan:
        finding_count += sum(len(device.findings) for device in network_scan.devices)
    if firmware_scan:
        finding_count += len(firmware_scan.findings)
    if passive_scan:
        finding_count += len(passive_scan.observations)
    graph_nodes = correlation.summary.get("nodes", 0) if correlation else 0
    graph_edges = correlation.summary.get("edges", 0) if correlation else 0
    firmware_extracted_files = len(firmware_scan.extracted_artifacts) if firmware_scan else 0
    firmware_certificates = len(firmware_scan.certificates) if firmware_scan else 0
    firmware_certificate_findings = len(firmware_scan.certificate_findings) if firmware_scan else 0
    firmware_config_hints = len(firmware_scan.config_hints) if firmware_scan else 0
    firmware_service_hints = len(firmware_scan.service_hints) if firmware_scan else 0
    firmware_components = len(firmware_scan.software_components) if firmware_scan else 0
    firmware_inference_hints = len(firmware_scan.inference_hints) if firmware_scan else 0
    firmware_extraction_provider = firmware_scan.extraction_metadata.provider if firmware_scan else ""

    return StatusArtifact(
        workspace=inspection.workspace,
        version=__version__,
        artifact_count=inspection.artifact_count,
        device_count=device_count,
        finding_count=finding_count,
        graph_nodes=graph_nodes,
        graph_edges=graph_edges,
        firmware_extracted_files=firmware_extracted_files,
        firmware_certificates=firmware_certificates,
        firmware_certificate_findings=firmware_certificate_findings,
        firmware_config_hints=firmware_config_hints,
        firmware_service_hints=firmware_service_hints,
        firmware_components=firmware_components,
        firmware_inference_hints=firmware_inference_hints,
        firmware_extraction_provider=firmware_extraction_provider,
        capabilities=_capability_inventory(),
        artifacts_present=sorted(item.name for item in inspection.artifacts if item.present),
    )


def _capability_inventory() -> list[CapabilityRecord]:
    capabilities = [
        CapabilityRecord(
            name="active-network-scan",
            category="discovery",
            summary="Active TCP and bounded UDP discovery with resumable workspace artifacts.",
        ),
        CapabilityRecord(
            name="discovery-module-architecture",
            category="discovery",
            summary="Pluggable per-host discovery modules for extending scan coverage safely.",
        ),
        CapabilityRecord(
            name="passive-provider-abstractions",
            category="discovery",
            summary="Passive observation provider and parser interfaces for future lab-safe listeners.",
        ),
        CapabilityRecord(
            name="passive-scan-command",
            category="discovery",
            summary="Optional passive scan workflow for parsing authorized traffic summaries into workspace artifacts.",
        ),
        CapabilityRecord(
            name="fingerprint-matching",
            category="enrichment",
            summary="Built-in and workspace override fingerprint matching with confidence tracking.",
        ),
        CapabilityRecord(
            name="offline-vulnerability-enrichment",
            category="enrichment",
            summary="Local JSON vulnerability feed matching for device and firmware components.",
        ),
        CapabilityRecord(
            name="firmware-extraction-analysis",
            category="analysis",
            summary="Safe archive extraction with bounded nested archive handling plus extracted-content analysis for firmware evidence, components, configs, certificates, and service hints.",
        ),
        CapabilityRecord(
            name="evidence-graph-reporting",
            category="reporting",
            summary="Graph correlation plus terminal, Markdown, JSON, HTML, and diff reporting.",
        ),
        CapabilityRecord(
            name="workspace-inspection",
            category="operations",
            summary="Structured workspace status and artifact inspection commands.",
        ),
    ]
    capabilities.sort(key=lambda item: (item.category, item.name))
    return capabilities


def _load_model(path: Path, model_type):  # type: ignore[no-untyped-def]
    if not path.exists():
        return None
    return model_type.model_validate(load_json(path))


def _normalize_workspace_path(workspace: Path) -> Path:
    normalized = normalize_path(workspace, field_name="workspace path")
    if normalized.exists() and not normalized.is_dir():
        raise ValueError(f"Workspace path must be a directory: {normalized}")
    return normalized

