import concurrent.futures
from dataclasses import dataclass, field
from typing import Dict, Iterable, List, Optional

from .localization.helpers import create_translated_finding
from .model import Finding, ScanRequest, ScanResult
from .orchestrator import CheckModule, build_default_modules


@dataclass
class SentinelScanner:
    """
    Coordinates execution of passive and low-impact security diagnostics.
    """

    max_workers: int = 4
    modules: Dict[str, CheckModule] = field(default_factory=lambda: {m.name: m for m in build_default_modules()})

    def register_module(self, module: CheckModule) -> None:
        self.modules[module.name] = module

    def run(self, request: ScanRequest, enabled_modules: Optional[Iterable[str]] = None) -> ScanResult:
        findings: List[Finding] = []

        modules = self._resolve_modules(enabled_modules)

        with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_map = {
                executor.submit(module.runner, request): module.name for module in modules
            }
            for future in concurrent.futures.as_completed(future_map):
                try:
                    result = future.result()
                    if isinstance(result, Iterable):
                        findings.extend(result)
                except Exception as exc:  # pragma: no cover - defensive logging hook
                    findings.append(
                        create_translated_finding(
                            check=future_map[future],
                            i18n_key="scanner.unhandled_error",
                            severity="medium",
                            i18n_params={
                                "module": future_map[future],
                                "error": repr(exc),
                            },
                        )
                    )

        return ScanResult(request=request, findings=sorted(findings, key=_finding_sort_key))

    def _resolve_modules(self, enabled_modules: Optional[Iterable[str]]) -> List[CheckModule]:
        if enabled_modules is None:
            return list(self.modules.values())

        selected = []
        for name in enabled_modules:
            module = self.modules.get(name)
            if module:
                selected.append(module)
        return selected


def _finding_sort_key(finding: Finding) -> tuple:
    severity_order = {"critical": 0, "high": 1, "medium": 2, "low": 3, "info": 4}
    return severity_order.get(finding.severity, 99), finding.check, finding.title
