# File: cpp_python_debug/core/symbol_analyzer.py
import os
import time  # Usato per analysis_timestamp e executable_timestamp
import logging
from typing import (
    Dict,
    Any,
    List,
    Optional,
    Callable,
    Type,
    Union,
    TYPE_CHECKING,
)  # Aggiunto Type, Union

# MODIFICA: Import assoluti (erano già per lo più corretti)
# GDBSession è importato per il type hint di gdb_session_class, anche se SymbolAnalyzer
# istanzierà specificamente GDBMISession se quella è la classe passata.
from cpp_python_debug.core.gdb_controller import GDBSession  # Per type hint Union
from cpp_python_debug.core.gdb_mi_session import (
    GDBMISession,
)  # Usato come default e per type hint
from cpp_python_debug.core.file_utils import calculate_file_checksum
from cpp_python_debug.core import file_utils

# AppSettings sarà passato come istanza, non importato direttamente qui se non per type hint
# from cpp_python_debug.core.config_manager import AppSettings # Solo per type hinting se necessario

if TYPE_CHECKING:  # Per evitare import circolari ma avere type hints
    from cpp_python_debug.core.config_manager import AppSettings

logger = logging.getLogger(__name__)  # Logger specifico per questo modulo


class SymbolAnalyzer:
    """
    Orchestrates the analysis of symbols from a target executable using GDB.
    Runs GDB in a temporary session to extract information.
    """

    def __init__(
        self,
        gdb_exe_path: str,
        app_settings_instance: "AppSettings",  # Usa il type hint stringato
        gdb_session_class: Type[
            Union[GDBSession, GDBMISession]
        ] = GDBMISession,  # Default a GDBMISession
    ):
        """
        Initializes the SymbolAnalyzer.

        Args:
            gdb_exe_path: Path to the GDB executable.
            app_settings_instance: An instance of AppSettings for retrieving timeouts.
            gdb_session_class: The class to use for GDB session (e.g., GDBSession or GDBMISession).
                               Defaults to GDBMISession for symbol analysis.
        """
        self.gdb_exe_path: str = gdb_exe_path
        self.app_settings: "AppSettings" = app_settings_instance  # Tipo corretto
        self._temp_gdb_session: Optional[Union[GDBSession, GDBMISession]] = (
            None  # Tipo corretto
        )
        self.gdb_session_class: Type[Union[GDBSession, GDBMISession]] = (
            gdb_session_class
        )
        logger.info(
            f"SymbolAnalyzer initialized with GDB session class: {self.gdb_session_class.__name__}"
        )

    def _get_timeout(self, category: str, key: str, default: int) -> int:
        """Helper to get timeout from app_settings."""
        # Assicura che app_settings sia disponibile
        if not self.app_settings:
            logger.warning(
                f"AppSettings not available in SymbolAnalyzer, using default timeout {default} for {category}/{key}"
            )
            return default
        return self.app_settings.get_setting(category, key, default)

    def analyze(
        self,
        target_exe_path: str,
        progress_callback: Optional[Callable[[str], None]] = None,
        status_callback: Optional[Callable[[str], None]] = None,
    ) -> Dict[
        str, Any
    ]:  # Restituisce un dizionario o un dizionario vuoto in caso di fallimento
        """
        Performs a full symbol analysis of the target executable using GDB.

        Args:
            target_exe_path: The full path to the target executable.
            progress_callback: A callback function (str) -> None to log detailed progress.
            status_callback: A callback function (str) -> None to update general status.

        Returns:
            A dictionary containing all analyzed symbol data, or an empty dict on critical failure.
        """
        analysis_data_dict: Dict[str, Any] = {
            "analyzed_executable_path": target_exe_path,
            "executable_checksum": None,
            "executable_timestamp": "N/A",
            "analysis_timestamp": "N/A",
            "gdb_version_info": "N/A",
            "symbols": {  # I valori ora sono liste di dizionari o liste vuote
                "functions": [],
                "functions_count": 0,
                "global_variables": [],
                "global_variables_count": 0,
                "types": [],
                "types_count": 0,
                "source_files": [],
                "source_files_count": 0,
            },
        }

        # Definisci callback di default se non forniti
        _progress_callback: Callable[[str], None] = (
            progress_callback
            if progress_callback
            else lambda msg: logger.info(f"SymbolAnalysis Progress: {msg}")
        )
        _status_callback: Callable[[str], None] = (
            status_callback
            if status_callback
            else lambda msg: logger.info(f"SymbolAnalysis Status: {msg}")
        )

        _progress_callback(
            f"Starting symbol analysis for: {os.path.basename(target_exe_path)}"
        )
        _status_callback(f"Initializing GDB for {os.path.basename(target_exe_path)}...")

        # Recupera i default dei timeout da AppSettings una volta
        default_timeouts = self.app_settings._get_default_settings().get("timeouts", {})

        try:
            self._temp_gdb_session = self.gdb_session_class(
                gdb_path=self.gdb_exe_path,
                executable_path=target_exe_path,
                # gdb_script_full_path e dumper_options non sono necessari per l'analisi MI
            )

            startup_timeout = self._get_timeout(
                "timeouts", "gdb_start", default_timeouts.get("gdb_start", 30)
            )
            # Timeout per comandi "info" possono essere lunghi, usiamo un valore più alto
            info_command_timeout = self._get_timeout(
                "timeouts",
                "program_run_continue",
                default_timeouts.get("program_run_continue", 120),
            )  # Riusa un timeout lungo

            self._temp_gdb_session.start(timeout=startup_timeout)
            _progress_callback("GDB session started for analysis.")

            if not self._temp_gdb_session.symbols_found:
                _progress_callback(
                    "WARNING: GDB reported no debugging symbols. Analysis results may be limited or empty."
                )
                # Non interrompere qui, alcuni comandi "info" potrebbero comunque funzionare parzialmente

            _status_callback("Fetching GDB version information...")
            _progress_callback("Fetching GDB version...")
            gdb_version = self._temp_gdb_session.get_gdb_version(
                timeout=self._get_timeout(
                    "timeouts", "gdb_command", default_timeouts.get("gdb_command", 30)
                )
            )
            analysis_data_dict["gdb_version_info"] = (
                gdb_version if gdb_version else "N/A"
            )
            _progress_callback(f"GDB Version: {analysis_data_dict['gdb_version_info']}")

            _status_callback("Fetching function list...")
            _progress_callback("Fetching function list from GDB...")
            # list_functions in GDBMISession ora restituisce List[Dict[str, Any]]
            functions_data = self._temp_gdb_session.list_functions(
                timeout=info_command_timeout
            )
            analysis_data_dict["symbols"]["functions"] = functions_data
            analysis_data_dict["symbols"]["functions_count"] = len(functions_data)
            _progress_callback(f"Found {len(functions_data)} functions.")

            _status_callback("Fetching global variables...")
            _progress_callback("Fetching global variables from GDB...")
            global_vars_data = self._temp_gdb_session.list_global_variables(
                timeout=info_command_timeout
            )
            analysis_data_dict["symbols"]["global_variables"] = global_vars_data
            analysis_data_dict["symbols"]["global_variables_count"] = len(
                global_vars_data
            )
            _progress_callback(f"Found {len(global_vars_data)} global variables.")

            _status_callback("Fetching types...")
            _progress_callback("Fetching types from GDB...")
            types_data = self._temp_gdb_session.list_types(timeout=info_command_timeout)
            analysis_data_dict["symbols"]["types"] = types_data
            analysis_data_dict["symbols"]["types_count"] = len(types_data)
            _progress_callback(f"Found {len(types_data)} types.")

            _status_callback("Fetching source files...")
            _progress_callback("Fetching source files from GDB...")
            source_files_data = self._temp_gdb_session.list_source_files(
                timeout=info_command_timeout
            )
            analysis_data_dict["symbols"]["source_files"] = source_files_data
            analysis_data_dict["symbols"]["source_files_count"] = len(source_files_data)
            _progress_callback(f"Found {len(source_files_data)} source files.")

            _status_callback("Calculating file checksum and timestamp...")
            _progress_callback("Calculating file checksum and timestamp...")
            analysis_data_dict["executable_checksum"] = (
                file_utils.calculate_file_checksum(target_exe_path)
            )  # Usa file_utils
            try:
                mtime = os.path.getmtime(target_exe_path)
                analysis_data_dict["executable_timestamp"] = time.strftime(
                    "%Y-%m-%d %H:%M:%S", time.localtime(mtime)
                )
            except OSError as e_mtime:
                _progress_callback(
                    f"Warning: Could not get timestamp for {target_exe_path}: {e_mtime}"
                )
                logger.warning(f"Could not get mtime for {target_exe_path}: {e_mtime}")

            _progress_callback(
                f"Checksum (MD5): {analysis_data_dict['executable_checksum'] or 'N/A'}"
            )
            _progress_callback(
                f"File Timestamp: {analysis_data_dict['executable_timestamp']}"
            )
            analysis_data_dict["analysis_timestamp"] = time.strftime(
                "%Y-%m-%d %H:%M:%S", time.localtime()
            )  # Ora locale
            _progress_callback(
                f"Analysis performed at: {analysis_data_dict['analysis_timestamp']}"
            )

            _status_callback("Symbol analysis successfully completed.")
            _progress_callback("\nSymbol analysis successfully completed.")
            return analysis_data_dict

        except (
            FileNotFoundError
        ) as e_fnf:  # Ad esempio, GDB non trovato da GDBMISession
            logger.error(
                f"FileNotFoundError during symbol analysis for '{target_exe_path}': {e_fnf}",
                exc_info=True,
            )
            _progress_callback(f"\nCRITICAL ERROR: File not found - {e_fnf}")
            _status_callback(f"Error: File not found - {e_fnf}")
            return {}  # Restituisce dizionario vuoto per indicare fallimento
        except (
            ConnectionError,
            TimeoutError,
        ) as e_gdb_comm:  # Eccezioni da GDBMISession
            logger.error(
                f"GDB Communication Error during symbol analysis for '{target_exe_path}': {e_gdb_comm}",
                exc_info=True,
            )
            _progress_callback(
                f"\nCRITICAL GDB ERROR: {type(e_gdb_comm).__name__} - {e_gdb_comm}"
            )
            _status_callback(f"Error communicating with GDB: {e_gdb_comm}")
            return {}
        except Exception as e_general:  # Catch-all per altri errori
            logger.error(
                f"Unexpected error during symbol analysis for '{target_exe_path}': {e_general}",
                exc_info=True,
            )
            _progress_callback(
                f"\nCRITICAL UNEXPECTED ERROR: {type(e_general).__name__} - {e_general}"
            )
            _status_callback(f"Unexpected error during analysis: {e_general}")
            return {}
        finally:
            if self._temp_gdb_session and self._temp_gdb_session.is_alive():
                _progress_callback("Closing GDB session used for analysis...")
                _status_callback("Closing GDB analysis session...")
                quit_timeout = self._get_timeout(
                    "timeouts", "gdb_quit", default_timeouts.get("gdb_quit", 10)
                )
                try:
                    self._temp_gdb_session.quit(timeout=quit_timeout)
                    _progress_callback("GDB analysis session closed.")
                except Exception as e_quit:
                    logger.error(
                        f"Error quitting GDB analysis session: {e_quit}", exc_info=True
                    )
                    _progress_callback(f"Error closing GDB analysis session: {e_quit}")
            self._temp_gdb_session = None  # Assicura che sia resettato
