"""SDK matcher - applies signatures against parsed APK data."""
import re
from typing import Dict, List
import os
from apk_analyzer.models import DEXData, ManifestData, NativeData, DetectedSDK, DetectionEvidence, SDKRisk


class SDKMatcher:
    def __init__(self, signatures: List[dict], dex: DEXData,
                 manifest: ManifestData, native: NativeData, versions: Dict[str, str]):
        self.signatures = signatures
        self.dex = dex
        self.manifest = manifest
        self.native = native
        self.versions = versions
        self._manifest_services_set = set(manifest.services)
        self._manifest_receivers_set = set(manifest.receivers)
        self._native_set = set(native.lib_names)

    def match(self) -> List[DetectedSDK]:
        results = []
        for sig in self.signatures:
            sdk = self._match_one(sig)
            if sdk:
                results.append(sdk)
        results.sort(key=lambda s: s.category)
        return results

    def _match_one(self, sig: dict) -> DetectedSDK | None:
        det = sig.get("detection", {}) or {}
        evidence = DetectionEvidence()
        matched = False

        # 1. Package prefixes (DEX class names)
        for prefix in det.get("package_prefixes", []) or []:
            if self.dex.has_package(prefix):
                evidence.matched_packages.append(prefix)
                matched = True

        # 2. Native libs
        for lib in det.get("native_libs", []) or []:
            if lib in self._native_set:
                evidence.matched_natives.append(lib)
                matched = True

        # 3. Manifest services
        for svc in det.get("manifest_services", []) or []:
            if svc in self._manifest_services_set:
                evidence.matched_manifest_services.append(svc)
                matched = True

        # 4. Manifest receivers
        for rcv in det.get("manifest_receivers", []) or []:
            if rcv in self._manifest_receivers_set:
                evidence.matched_manifest_receivers.append(rcv)
                matched = True

        # 5. String pool patterns
        for pattern in det.get("string_patterns", []) or []:
            for s in self.dex.string_pool:
                if pattern in s:
                    evidence.matched_string_patterns.append(pattern)
                    matched = True
                    break

        if not matched:
            return None

        # Confidence
        total = (len(evidence.matched_packages) + len(evidence.matched_natives) +
                 len(evidence.matched_manifest_services) + len(evidence.matched_manifest_receivers))
        confidence = "high" if total >= 2 else "medium" if total == 1 else "low"

        # Version extraction
        version, version_source = None, None
        ve = sig.get("version_extraction", {}) or {}

        # BuildConfig
        bc = ve.get("buildconfig", {}) or {}
        if bc and bc.get("class_pattern"):
            pattern = bc["class_pattern"]
            field_name = bc.get("field", "VERSION_NAME")
            for cls, ver in self.versions.items():
                if pattern in cls or cls in pattern:
                    version = ver
                    version_source = "buildconfig"
                    break

        # META-INF
        if not version:
            mi_key = ve.get("metainf_file")
            if mi_key and mi_key in self.versions:
                version = self.versions[mi_key]
                version_source = "metainf"

        # Package prefix version guess from versions dict
        if not version and evidence.matched_packages:
            for prefix in evidence.matched_packages:
                for key, ver in self.versions.items():
                    if key.startswith(prefix):
                        version = ver
                        version_source = "buildconfig"
                        break
                if version: break

        # Risk
        risk_data = sig.get("risk", {}) or {}
        risk = SDKRisk(
            permissions=risk_data.get("permissions", []) or [],
            data_collection=risk_data.get("data_collection", []) or [],
            privacy_risk=risk_data.get("privacy_risk", "unknown"),
            notes=risk_data.get("notes", ""),
        )

        return DetectedSDK(
            id=sig.get("id", ""),
            name=sig.get("name", ""),
            category=sig.get("category", "other"),
            vendor=sig.get("vendor", ""),
            description=sig.get("description", ""),
            confidence=confidence,
            version=version,
            version_source=version_source,
            detection_evidence=evidence,
            risk=risk,
        )
