from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any


REQUIRED_RUNTIME_KEYS = {
    "adapter",
    "device_id",
    "pairing_state",
    "heartbeat",
    "passive_only",
    "trust_state",
    "trust_valid",
    "trust_expired",
    "trust_revoked",
    "update_channel",
    "update_eligible",
    "update_manifest_valid",
    "rollback_allowed",
    "audit_integrity_ok",
    "mode",
}

REQUIRED_HEARTBEAT_KEYS = {
    "adapter",
    "heartbeat",
    "trust_state",
    "trust_revoked",
    "metadata_only",
    "command_channel",
    "execution_channel",
    "passive_only",
}

REQUIRED_BUNDLE_LAYER_KEYS = {
    "governance_snapshot",
    "policy_transparency_report",
    "audit_integrity_summary",
    "visibility_summary",
    "trust_governance_state",
    "update_governance_state",
    "bundle_manifest",
}


@dataclass(slots=True)
class PlatformParityResult:
    ok: bool
    checks: dict[str, bool]
    issues: list[str]


def run_platform_parity_checks(distributions: dict[str, dict[str, Any]]) -> PlatformParityResult:
    checks = {
        "schema_parity": True,
        "runtime_parity": True,
        "policy_parity": True,
        "visibility_parity": True,
        "governance_parity": True,
        "evidence_parity": True,
    }
    issues: list[str] = []

    baseline_snapshot_keys: set[str] | None = None

    for platform, payload in distributions.items():
        runtime = payload.get("runtime_status", {})
        runtime_keys = set(runtime.keys())
        if not REQUIRED_RUNTIME_KEYS.issubset(runtime_keys):
            checks["schema_parity"] = False
            issues.append(f"{platform}: runtime schema missing required keys")

        heartbeat = runtime.get("heartbeat", {})
        if not REQUIRED_HEARTBEAT_KEYS.issubset(set(heartbeat.keys())):
            checks["runtime_parity"] = False
            issues.append(f"{platform}: heartbeat schema missing required keys")

        passive_only = runtime.get("passive_only")
        if not isinstance(passive_only, bool):
            checks["runtime_parity"] = False
            issues.append(f"{platform}: runtime passive_only must be boolean")

        mode = str(runtime.get("mode") or "").strip().lower()
        if passive_only is True and mode != "passive_only":
            checks["runtime_parity"] = False
            issues.append(f"{platform}: passive_only=true requires mode=passive_only")
        if passive_only is False and mode == "passive_only":
            checks["runtime_parity"] = False
            issues.append(f"{platform}: passive_only=false conflicts with mode=passive_only")

        policy = payload.get("policy_transparency_report", {})
        if not policy.get("evaluation_decision"):
            checks["policy_parity"] = False
            issues.append(f"{platform}: policy transparency decision missing")

        snapshot = payload.get("governance_snapshot", {})
        snapshot_passive = snapshot.get("passive_only")
        if not isinstance(snapshot_passive, bool):
            checks["governance_parity"] = False
            issues.append(f"{platform}: governance snapshot passive_only must be boolean")
        elif isinstance(passive_only, bool) and snapshot_passive != passive_only:
            checks["governance_parity"] = False
            issues.append(f"{platform}: governance/runtime passive_only mismatch")

        bundle = payload.get("evidence_bundle", {})
        bundle_content = bundle.get("bundle_content", {})
        if not REQUIRED_BUNDLE_LAYER_KEYS.issubset(set(bundle_content.keys())):
            checks["evidence_parity"] = False
            issues.append(f"{platform}: evidence bundle missing required layers")

        if "visibility_summary" not in bundle_content:
            checks["visibility_parity"] = False
            issues.append(f"{platform}: visibility summary missing")

        snapshot_keys = set(snapshot.keys())
        if baseline_snapshot_keys is None:
            baseline_snapshot_keys = snapshot_keys
        elif snapshot_keys != baseline_snapshot_keys:
            checks["governance_parity"] = False
            issues.append(f"{platform}: snapshot keys differ from baseline")

    return PlatformParityResult(ok=all(checks.values()), checks=checks, issues=issues)


def scan_forbidden_patterns(paths: list[Path], patterns: list[str]) -> list[str]:
    findings: list[str] = []
    lowered_patterns = [p.lower() for p in patterns]

    for base in paths:
        if not base.exists():
            continue
        for file_path in sorted(base.rglob("*")):
            if not file_path.is_file():
                continue
            if file_path.suffix.lower() not in {".py", ".ps1", ".sh", ".md", ".txt"}:
                continue
            text = file_path.read_text(encoding="utf-8", errors="ignore").lower()
            for pattern in lowered_patterns:
                if pattern in text:
                    findings.append(f"{file_path}:{pattern}")
    return findings
