"""Validation helpers for fingerprint intelligence data."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

from sentra.fingerprints.matcher import FingerprintEntry, load_fingerprint_entries, normalize_token


@dataclass(frozen=True)
class FingerprintValidationIssue:
    level: str
    fingerprint: str
    message: str


@dataclass(frozen=True)
class FingerprintValidationResult:
    entries_checked: int
    issue_count: int
    issues: list[FingerprintValidationIssue]


def validate_fingerprint_entries(
    workspace: Path | None = None,
    *,
    user_override_paths: list[Path] | None = None,
) -> FingerprintValidationResult:
    entries = load_fingerprint_entries(workspace, user_override_paths=user_override_paths)
    issues: list[FingerprintValidationIssue] = []
    seen_names: dict[str, FingerprintEntry] = {}

    for entry in entries:
        if not entry.services and not entry.products and not entry.metadata_tokens and not entry.endpoint_patterns:
            issues.append(_issue("error", entry, "Fingerprint has no usable matching signals."))
        if entry.confidence < 0.3:
            issues.append(_issue("warning", entry, "Fingerprint confidence is unusually low."))
        if entry.vendor.lower() == "unknown" and entry.model.lower() == "unknown":
            issues.append(_issue("warning", entry, "Fingerprint resolves to unknown vendor and model."))
        if entry.name in seen_names:
            issues.append(_issue("warning", entry, f"Fingerprint name duplicates {seen_names[entry.name].source} data."))
        seen_names[entry.name] = entry
        _check_duplicate_values(entry, issues)

    issues.sort(key=lambda item: (item.level, item.fingerprint, item.message))
    return FingerprintValidationResult(entries_checked=len(entries), issue_count=len(issues), issues=issues)


def _check_duplicate_values(entry: FingerprintEntry, issues: list[FingerprintValidationIssue]) -> None:
    for label, values in (
        ("services", entry.services),
        ("products", entry.products),
        ("metadata tokens", entry.metadata_tokens),
        ("aliases", entry.aliases),
        ("optional indicators", entry.optional_indicators),
        ("conflict indicators", entry.conflict_indicators),
        ("protocol hints", entry.protocol_hints),
    ):
        if len(values) != len(set(values)):
            issues.append(_issue("warning", entry, f"Fingerprint contains duplicate {label}."))
    if not entry.family:
        issues.append(_issue("warning", entry, "Fingerprint family should not be empty."))
    for endpoint in entry.endpoint_patterns:
        if endpoint != endpoint.lower():
            issues.append(_issue("warning", entry, "Endpoint patterns should be lowercase for deterministic matching."))
        if not endpoint.startswith("/"):
            issues.append(_issue("warning", entry, "Endpoint patterns should begin with '/'."))
    for token in [
        *entry.services,
        *entry.products,
        *entry.metadata_tokens,
        *entry.aliases,
        *entry.optional_indicators,
        *entry.conflict_indicators,
        *entry.protocol_hints,
    ]:
        if token != normalize_token(token):
            issues.append(_issue("warning", entry, f"Token '{token}' is not normalized."))
    for group in entry.evidence_groups:
        if group.weight <= 0:
            issues.append(_issue("warning", entry, f"Evidence group '{group.name}' has a non-positive weight."))
        if not any((group.services, group.products, group.metadata_tokens, group.endpoint_patterns, group.protocol_hints, group.aliases)):
            issues.append(_issue("warning", entry, f"Evidence group '{group.name}' has no usable signals."))


def _issue(level: str, entry: FingerprintEntry, message: str) -> FingerprintValidationIssue:
    return FingerprintValidationIssue(level=level, fingerprint=entry.name, message=message)

