"""Exploit mitigation analyzer."""

from typing import Any, cast

from ..abstractions import BaseAnalyzer
from ..abstractions.command_helper_mixin import CommandHelperMixin
from ..domain.services.exploit_mitigation import (
    build_entry_point_vulnerability,
    build_pe_info,
    build_relocations_vulnerability,
    build_safe_seh_entry,
    read_u32,
    update_mitigations_from_guard_flags,
)
from ..infrastructure.logging import get_logger
from .exploit_mitigation_access import ExploitMitigationAccessMixin
from .exploit_mitigation_flow import (
    analyze_exploit_mitigations,
    check_dll_characteristics as _check_dll_characteristics_flow,
    check_load_config as _check_load_config_flow,
    check_safe_seh as _check_safe_seh_flow,
    get_load_config_dir as _get_load_config_dir_flow,
    is_pe_64bit as _is_pe_64bit_flow,
    load_config_present as _load_config_present_flow,
    read_load_config_data as _read_load_config_data_flow,
)
from .exploit_mitigation_support import (
    check_authenticode as _check_authenticode_impl,
    check_pe_security_features as _check_pe_security_features_impl,
    check_stack_cookies as _check_stack_cookies_impl,
    find_gs_indicator as _find_gs_indicator_impl,
    has_security_cookie_imports as _has_security_cookie_imports_impl,
    stack_cookie_entry as _stack_cookie_entry_impl,
)
from .exploit_mitigation_rules import (
    generate_recommendations,
)
from .security_scoring import build_security_score

logger = get_logger(__name__)


class ExploitMitigationAnalyzer(ExploitMitigationAccessMixin, CommandHelperMixin, BaseAnalyzer):
    """Analyzes exploit mitigation features in PE files."""

    def __init__(self, adapter: Any) -> None:
        """
        Initialize the Exploit Mitigation analyzer.

        Args:
            r2: Radare2 instance
        """
        super().__init__(adapter=adapter)

    def analyze(self) -> dict[str, Any]:
        """Analyze exploit mitigation features in the PE file."""
        return analyze_exploit_mitigations(self, logger=logger)

    def _check_dll_characteristics(self, result: dict[str, Any]) -> None:
        """Check DllCharacteristics flags for security features."""
        _check_dll_characteristics_flow(self, result, logger=logger)

    def _check_safe_seh(self, result: dict[str, Any]) -> None:
        """Check for SafeSEH (Safe Structured Exception Handling)."""
        _check_safe_seh_flow(self, result, logger=logger)

    def _check_load_config(self, result: dict[str, Any]) -> None:
        """Check Load Configuration Directory for security features."""
        _check_load_config_flow(self, result, logger=logger)

    def _has_no_seh_flag(self, result: dict[str, Any]) -> bool:
        return bool(result.get("dll_characteristics", {}).get("flags", {}).get("NO_SEH", False))

    def _safe_seh_entry(
        self, enabled: bool, note: str | None = None, error: str | None = None
    ) -> dict[str, Any]:
        return build_safe_seh_entry(enabled=enabled, note=note, error=error)

    def _set_safe_seh(self, result: dict[str, Any], enabled: bool, note: str) -> None:
        result["mitigations"]["SafeSEH"] = self._safe_seh_entry(enabled=enabled, note=note)

    def _get_load_config_dir(self) -> dict[str, Any] | None:
        return _get_load_config_dir_flow(self)

    def _load_config_present(self, load_config_dir: dict[str, Any]) -> bool:
        return _load_config_present_flow(self, load_config_dir)

    def _is_pe_64bit(self) -> bool:
        return _is_pe_64bit_flow(self)

    def _read_load_config_data(self, offset: int, size: int) -> list[int]:
        return _read_load_config_data_flow(self, offset, size)

    def _parse_load_config(
        self,
        config_data: list[int],
        size: int,
        offset: int,
        is_64bit: bool,
        result: dict[str, Any],
    ) -> dict[str, Any]:
        from ..domain.services.exploit_mitigation import parse_load_config

        return parse_load_config(config_data, size, offset, is_64bit, result["mitigations"])

    def _read_u32(self, data: list[int], offset: int) -> int:
        return read_u32(data, offset)

    def _parse_security_cookie(
        self, load_config: dict[str, Any], config_data: list[int], is_64bit: bool
    ) -> None:
        cookie_offset = 88 if is_64bit else 60
        if len(config_data) <= cookie_offset + 4:
            return
        security_cookie = self._read_u32(config_data, cookie_offset)
        load_config["security_cookie"] = hex(security_cookie) if security_cookie else None

    def _parse_guard_flags(
        self,
        load_config: dict[str, Any],
        config_data: list[int],
        config_size: int,
        is_64bit: bool,
        result: dict[str, Any],
    ) -> None:
        if config_size < 148:
            return
        guard_flags_offset = 144 if is_64bit else 140
        if len(config_data) <= guard_flags_offset + 4:
            return
        guard_flags = self._read_u32(config_data, guard_flags_offset)
        load_config["guard_flags"] = {
            "value": hex(guard_flags),
            "cf_instrumented": bool(guard_flags & 0x100),
            "cf_function_table_present": bool(guard_flags & 0x200),
            "cf_long_jump_table_present": bool(guard_flags & 0x400),
            "rf_instrumented": bool(guard_flags & 0x20000),
            "rf_enable": bool(guard_flags & 0x40000),
            "rf_strict": bool(guard_flags & 0x80000),
            "retpoline_present": bool(guard_flags & 0x100000),
            "eh_continuation_table_present": bool(guard_flags & 0x400000),
        }
        self._update_mitigations_from_guard_flags(result, guard_flags)

    def _update_mitigations_from_guard_flags(
        self, result: dict[str, Any], guard_flags: int
    ) -> None:
        update_mitigations_from_guard_flags(result["mitigations"], guard_flags)

    def _check_stack_cookies(self, result: dict[str, Any]) -> None:
        """Check for stack cookies (GS security)."""
        _check_stack_cookies_impl(self, result, logger=logger)

    def _stack_cookie_entry(
        self, enabled: bool, cookie: str | None = None, note: str | None = None
    ) -> dict[str, Any]:
        return _stack_cookie_entry_impl(enabled=enabled, cookie=cookie, note=note)

    def _has_security_cookie_imports(self) -> bool:
        return _has_security_cookie_imports_impl(self._get_imports())

    def _find_gs_indicator(self) -> str | None:
        return _find_gs_indicator_impl(self._get_strings())

    def _check_authenticode(self, result: dict[str, Any]) -> None:
        """Check if binary is signed with Authenticode."""
        _check_authenticode_impl(self, result, logger=logger)

    def _check_pe_security_features(self, result: dict[str, Any]) -> None:
        """Check additional PE header security features."""
        _check_pe_security_features_impl(self, result, logger=logger)

    def _get_pe_header(self) -> dict[str, Any] | None:
        pe_header = self._cmdj("ihj", {})
        if not pe_header:
            return None
        if isinstance(pe_header, list):
            return pe_header[0] if pe_header else None
        return pe_header if isinstance(pe_header, dict) else None

    def _check_relocations_stripped(self, result: dict[str, Any], characteristics: int) -> None:
        vulnerability = build_relocations_vulnerability(
            bool(result["mitigations"].get("ASLR", {}).get("enabled")),
            characteristics,
        )
        if vulnerability:
            result["vulnerabilities"].append(vulnerability)

    def _set_pe_info(
        self, result: dict[str, Any], optional_header: dict[str, Any], is_dll: bool
    ) -> None:
        pe_info = build_pe_info(optional_header, is_dll)
        if pe_info:
            result["pe_info"] = pe_info

    def _check_entry_point_section(
        self, result: dict[str, Any], optional_header: dict[str, Any]
    ) -> None:
        entry_point = optional_header.get("entry_point", 0)
        image_base = optional_header.get("image_base", 0)
        sections = self._get_sections()
        if not (sections and isinstance(sections, list) and entry_point > 0):
            return
        entry_rva = entry_point - image_base if entry_point > image_base else entry_point
        entry_in_exec_section = self._entry_point_in_exec_section(sections, entry_rva)
        vulnerability = build_entry_point_vulnerability(entry_in_exec_section)
        if vulnerability:
            result["vulnerabilities"].append(vulnerability)

    def _entry_point_in_exec_section(self, sections: list[dict[str, Any]], entry_rva: int) -> bool:
        for section in sections:
            start = section.get("vaddr", 0)
            end = start + section.get("vsize", 0)
            if start <= entry_rva < end:
                perm = str(section.get("perm", ""))
                return perm.startswith("--x") or perm.startswith("-wx")
        return False

    def _calculate_security_score(self, result: dict[str, Any]) -> None:
        """Calculate overall security score based on enabled mitigations."""
        result["security_score"] = build_security_score(result)

    def _generate_recommendations(self, result: dict[str, Any]) -> None:
        """Generate security recommendations based on analysis."""
        result["recommendations"] = generate_recommendations(result)
