# File: cpp_python_debug/gui/dialogs.py
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext  # scrolledtext è usato
import logging
from typing import List, Optional, Dict, Any, Union, Callable  # Aggiunto Callable

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


class SymbolAnalysisProgressDialog(tk.Toplevel):
    """
    Dialog to show progress of symbol analysis.
    """

    def __init__(
        self, parent: tk.Widget
    ):  # tk.Widget è un buon tipo generico per il parent
        super().__init__(parent)
        self.title("Symbol Analysis")
        self.parent_window = parent

        window_width = 600
        window_height = 400

        # Assicurati che il parent sia visibile e abbia una geometria prima di centrare
        self.parent_window.update_idletasks()  # Importante per ottenere geometrie corrette del parent

        parent_x = self.parent_window.winfo_x()
        parent_y = self.parent_window.winfo_y()
        parent_width = self.parent_window.winfo_width()
        parent_height = self.parent_window.winfo_height()

        position_x = parent_x + (parent_width // 2) - (window_width // 2)
        position_y = parent_y + (parent_height // 2) - (window_height // 2)

        # Assicurati che le posizioni non siano negative (es. se il parent è molto piccolo o fuori schermo)
        position_x = max(0, position_x)
        position_y = max(0, position_y)

        self.geometry(f"{window_width}x{window_height}+{position_x}+{position_y}")

        self.transient(parent)
        self.grab_set()
        self.protocol("WM_DELETE_WINDOW", self._on_attempt_close)
        self.analysis_can_be_closed = False  # Flag per controllare la chiusura

        main_frame = ttk.Frame(self, padding="10")
        main_frame.pack(expand=True, fill=tk.BOTH)

        self.log_text_widget = scrolledtext.ScrolledText(
            main_frame, wrap=tk.WORD, height=15, state=tk.DISABLED, font=("Consolas", 9)
        )
        self.log_text_widget.pack(pady=5, padx=5, fill=tk.BOTH, expand=True)

        self.status_label_var = tk.StringVar(value="Initializing symbol analysis...")
        ttk.Label(main_frame, textvariable=self.status_label_var, justify=tk.LEFT).pack(
            pady=(5, 2), fill=tk.X
        )

        self.progressbar = ttk.Progressbar(main_frame, mode="indeterminate", length=300)
        self.progressbar.pack(pady=(0, 10), fill=tk.X, padx=5)
        self.progressbar.start(15)  # Intervallo di animazione in ms

        self.close_button = ttk.Button(
            main_frame,
            text="Close",
            command=self._on_close_button_click,
            state=tk.DISABLED,
        )
        self.close_button.pack(pady=5)

        self.update_idletasks()

    def _on_attempt_close(self):
        if not self.analysis_can_be_closed:
            messagebox.showwarning(
                "Analysis in Progress",
                "Symbol analysis is still in progress. Please wait for it to complete or fail.",
                parent=self,
            )  # Specifica parent per il messagebox
        else:
            self.destroy()

    def _on_close_button_click(self):
        self.destroy()

    def log_message(self, message: str):
        if not self.winfo_exists():
            return  # Non fare nulla se il widget è distrutto
        self.log_text_widget.config(state=tk.NORMAL)
        self.log_text_widget.insert(tk.END, message + "\n")
        self.log_text_widget.see(tk.END)
        self.log_text_widget.config(state=tk.DISABLED)
        try:
            self.update_idletasks()  # Forza l'aggiornamento della GUI
        except (
            tk.TclError
        ):  # Può accadere se il widget viene distrutto concorrentemente
            logger.warning(
                "TclError during log_message in SymbolAnalysisProgressDialog, widget might be destroyed."
            )
            pass

    def set_status(self, status_message: str):
        if not self.winfo_exists():
            return
        self.status_label_var.set(status_message)
        try:
            self.update_idletasks()
        except tk.TclError:
            logger.warning(
                "TclError during set_status in SymbolAnalysisProgressDialog, widget might be destroyed."
            )
            pass

    def analysis_complete_or_failed(self, success: bool):
        if not self.winfo_exists():
            return
        self.progressbar.stop()
        self.progressbar.config(mode="determinate")  # Cambia in modalità determinata
        self.progressbar["value"] = 100 if success else 0  # Imposta al 100% o 0%
        self.analysis_can_be_closed = True
        self.close_button.config(state=tk.NORMAL)
        if success:
            self.set_status("Analysis complete. You can close this window.")
        else:
            self.set_status(
                "Analysis failed or was aborted. Check log. You can close this window."
            )


class SymbolListViewerDialog(tk.Toplevel):
    # (Implementazione come prima, ma con type hinting e logica di centratura migliorati)
    def __init__(
        self,
        parent: tk.Widget,
        symbols: List[Union[str, Dict[str, Any]]],
        title: str = "Symbol List",
        allow_multiple_selection: bool = False,
        return_full_dict: bool = False,
    ):
        super().__init__(parent)
        self.title(title)
        self.parent_window = parent
        self.result: Optional[
            Union[str, Dict[str, Any], List[Union[str, Dict[str, Any]]]]
        ] = None
        self.allow_multiple_selection = allow_multiple_selection
        self.return_full_dict = return_full_dict

        window_width = 800
        window_height = 550

        self.parent_window.update_idletasks()
        parent_x = self.parent_window.winfo_x()
        parent_y = self.parent_window.winfo_y()
        parent_width = self.parent_window.winfo_width()
        parent_height = self.parent_window.winfo_height()
        position_x = max(0, parent_x + (parent_width // 2) - (window_width // 2))
        position_y = max(0, parent_y + (parent_height // 2) - (window_height // 2))
        self.geometry(f"{window_width}x{window_height}+{position_x}+{position_y}")

        self.transient(parent)
        self.grab_set()

        self._original_symbols_data = symbols
        self._currently_displayed_data: List[Union[str, Dict[str, Any]]] = []
        self.is_list_of_dicts = bool(
            self._original_symbols_data
            and isinstance(self._original_symbols_data[0], dict)
        )

        main_frame = ttk.Frame(self, padding="10")
        main_frame.pack(expand=True, fill=tk.BOTH)
        main_frame.rowconfigure(1, weight=1)  # Riga del Treeview si espande
        main_frame.columnconfigure(0, weight=1)  # Colonna del Treeview si espande

        filter_frame = ttk.Frame(main_frame)
        filter_frame.grid(row=0, column=0, sticky="ew", pady=(0, 5))
        ttk.Label(filter_frame, text="Filter:").pack(side=tk.LEFT, padx=(0, 5))
        self.filter_var = tk.StringVar()
        self.filter_var.trace_add("write", self._apply_filter)
        ttk.Entry(filter_frame, textvariable=self.filter_var, width=40).pack(
            side=tk.LEFT, expand=True, fill=tk.X
        )

        select_mode = (
            "extended" if self.allow_multiple_selection else "browse"
        )  # browse per singola selezione
        self.treeview = ttk.Treeview(
            main_frame, selectmode=select_mode, show="headings"
        )
        # Configurazione colonne treeview
        self.treeview.column(
            "#0", width=0, stretch=tk.NO
        )  # Nasconde la prima colonna fittizia
        self.treeview.heading("#0", text="")
        self.treeview.grid(row=1, column=0, sticky="nsew")

        self._setup_treeview_columns()  # Imposta le colonne reali

        scrollbar_y = ttk.Scrollbar(
            main_frame, orient=tk.VERTICAL, command=self.treeview.yview
        )
        scrollbar_y.grid(row=1, column=1, sticky="ns")
        self.treeview.configure(yscrollcommand=scrollbar_y.set)

        scrollbar_x = ttk.Scrollbar(
            main_frame, orient=tk.HORIZONTAL, command=self.treeview.xview
        )
        scrollbar_x.grid(row=2, column=0, sticky="ew")  # Sotto il treeview
        self.treeview.configure(xscrollcommand=scrollbar_x.set)

        self._populate_treeview(self._original_symbols_data)
        self.treeview.bind("<Double-1>", self._on_ok)  # Doppio click per confermare

        button_frame = ttk.Frame(main_frame, padding=(0, 10, 0, 0))
        button_frame.grid(
            row=3, column=0, columnspan=2, sticky="e"
        )  # columnspan=2 se scrollbar_y è col 1
        ttk.Button(button_frame, text="OK", command=self._on_ok).pack(
            side=tk.RIGHT, padx=5
        )
        ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(
            side=tk.RIGHT
        )

        self.protocol("WM_DELETE_WINDOW", self._on_cancel)
        # self.wait_window() # Rimosso, gestito dal chiamante

    def _setup_treeview_columns(self):
        # (Implementazione come prima)
        if self.is_list_of_dicts and self._original_symbols_data:
            first_item = self._original_symbols_data[0]
            if isinstance(first_item, dict):
                columns = list(first_item.keys())
                self.treeview["columns"] = columns
                for col_name in columns:
                    text_header = col_name.replace("_", " ").title()
                    self.treeview.heading(col_name, text=text_header, anchor=tk.W)
                    if col_name == "name":
                        self.treeview.column(
                            col_name, width=200, minwidth=120, stretch=tk.YES
                        )
                    elif col_name == "signature":
                        self.treeview.column(
                            col_name, width=300, minwidth=150, stretch=tk.YES
                        )
                    elif col_name == "file" or col_name == "path":
                        self.treeview.column(
                            col_name, width=250, minwidth=150, stretch=tk.YES
                        )
                    elif col_name == "line":
                        self.treeview.column(
                            col_name, width=60, minwidth=40, stretch=tk.NO
                        )
                    elif col_name == "type":
                        self.treeview.column(
                            col_name, width=150, minwidth=100, stretch=tk.YES
                        )
                    elif col_name == "status":
                        self.treeview.column(
                            col_name, width=80, minwidth=70, stretch=tk.NO
                        )
                    elif col_name == "definition":
                        self.treeview.column(
                            col_name, width=300, minwidth=150, stretch=tk.YES
                        )
                    else:
                        self.treeview.column(
                            col_name, width=120, minwidth=80, stretch=tk.YES
                        )
                return
        self.treeview["columns"] = ["value"]
        self.treeview.heading("value", text="Value", anchor=tk.W)
        self.treeview.column("value", width=700, minwidth=200, stretch=tk.YES)

    def _populate_treeview(self, symbols_to_show: List[Union[str, Dict[str, Any]]]):
        # (Implementazione come prima)
        self.treeview.delete(*self.treeview.get_children())
        self._currently_displayed_data = symbols_to_show

        for idx, item_data in enumerate(symbols_to_show):
            iid = str(idx)
            if self.is_list_of_dicts and isinstance(item_data, dict):
                values_for_row = [
                    item_data.get(col, "") for col in self.treeview["columns"]
                ]
                self.treeview.insert("", tk.END, iid=iid, values=values_for_row)
            else:
                self.treeview.insert("", tk.END, iid=iid, values=[str(item_data)])

    def _apply_filter(self, *args):
        # (Implementazione come prima)
        filter_text = self.filter_var.get().lower()
        if not filter_text:
            self._populate_treeview(self._original_symbols_data)
        else:
            filtered_list: List[Union[str, Dict[str, Any]]] = []
            if self.is_list_of_dicts:
                for (
                    item_data_unfiltered
                ) in self._original_symbols_data:  # Itera sempre sulla lista originale
                    if isinstance(item_data_unfiltered, dict):
                        if any(
                            str(v).lower().find(filter_text) != -1
                            for v in item_data_unfiltered.values()
                        ):
                            filtered_list.append(item_data_unfiltered)
            else:
                filtered_list = [
                    s
                    for s in self._original_symbols_data
                    if str(s).lower().find(filter_text) != -1
                ]
            self._populate_treeview(filtered_list)

    def _on_ok(self, event: Optional[tk.Event] = None):  # Aggiunto tipo per event
        # (Implementazione come prima)
        selected_iids = self.treeview.selection()
        if not selected_iids:
            self.result = [] if self.allow_multiple_selection else None
            self.destroy()
            return

        selected_items_to_return = []
        for iid_str in selected_iids:
            try:
                current_list_index = int(iid_str)
                if 0 <= current_list_index < len(self._currently_displayed_data):
                    selected_data_item_from_current_list = (
                        self._currently_displayed_data[current_list_index]
                    )
                    if self.return_full_dict:
                        selected_items_to_return.append(
                            selected_data_item_from_current_list
                        )
                    else:
                        if isinstance(selected_data_item_from_current_list, dict):
                            name_to_return = selected_data_item_from_current_list.get(
                                "name"
                            )
                            if name_to_return is None:
                                name_to_return = (
                                    selected_data_item_from_current_list.get("path")
                                )
                            if name_to_return is None:
                                name_to_return = str(
                                    next(
                                        iter(
                                            selected_data_item_from_current_list.values()
                                        ),
                                        "",
                                    )
                                )
                            selected_items_to_return.append(name_to_return)
                        else:
                            selected_items_to_return.append(
                                str(selected_data_item_from_current_list)
                            )
                else:
                    logger.warning(
                        f"Selected iid index {current_list_index} out of bounds for _currently_displayed_data (len: {len(self._currently_displayed_data)})"
                    )
            except ValueError:
                logger.error(f"Could not convert iid '{iid_str}' to int.")
            except Exception as e:
                logger.error(f"Error processing selected item with iid {iid_str}: {e}")

        if self.allow_multiple_selection:
            self.result = selected_items_to_return
        else:
            self.result = (
                selected_items_to_return[0] if selected_items_to_return else None
            )
        self.destroy()

    def _on_cancel(self):
        # (Implementazione come prima)
        self.result = [] if self.allow_multiple_selection else None
        self.destroy()


class FunctionSelectorDialog(tk.Toplevel):
    # (Implementazione come prima, ma con type hinting e logica di centratura migliorati)
    def __init__(
        self,
        parent: tk.Widget,
        functions_list: List[str],
        title: str = "Select Function",
    ):
        super().__init__(parent)
        self.title(title)
        self.parent_window = parent
        self.result: Optional[str] = None

        window_width = 500
        window_height = 400

        self.parent_window.update_idletasks()
        parent_x = self.parent_window.winfo_x()
        parent_y = self.parent_window.winfo_y()
        parent_width = self.parent_window.winfo_width()
        parent_height = self.parent_window.winfo_height()
        position_x = max(0, parent_x + (parent_width // 2) - (window_width // 2))
        position_y = max(0, parent_y + (parent_height // 2) - (window_height // 2))
        self.geometry(f"{window_width}x{window_height}+{position_x}+{position_y}")

        self.transient(parent)
        self.grab_set()

        main_frame = ttk.Frame(self, padding="10")
        main_frame.pack(expand=True, fill=tk.BOTH)
        main_frame.rowconfigure(1, weight=1)  # Riga della Listbox si espande
        main_frame.columnconfigure(0, weight=1)  # Colonna della Listbox si espande

        filter_frame = ttk.Frame(main_frame)
        filter_frame.grid(row=0, column=0, columnspan=2, sticky="ew", pady=(0, 5))
        ttk.Label(filter_frame, text="Filter:").pack(side=tk.LEFT, padx=(0, 5))
        self.filter_var = tk.StringVar()
        self.filter_var.trace_add("write", self._apply_filter_to_listbox)
        ttk.Entry(filter_frame, textvariable=self.filter_var, width=40).pack(
            side=tk.LEFT, expand=True, fill=tk.X
        )

        self.listbox = tk.Listbox(
            main_frame, selectmode=tk.SINGLE
        )  # selectmode SINGLE per Listbox
        self.listbox.grid(row=1, column=0, sticky="nsew")

        scrollbar = ttk.Scrollbar(
            main_frame, orient=tk.VERTICAL, command=self.listbox.yview
        )
        scrollbar.grid(row=1, column=1, sticky="ns")
        self.listbox.configure(yscrollcommand=scrollbar.set)

        self._original_functions_list = sorted(
            functions_list
        )  # Salva e ordina l'originale
        self._populate_listbox(self._original_functions_list)

        self.listbox.bind("<Double-Button-1>", self._on_ok)

        button_frame = ttk.Frame(main_frame, padding=(0, 10, 0, 0))
        button_frame.grid(row=2, column=0, columnspan=2, sticky="e")
        ttk.Button(button_frame, text="OK", command=self._on_ok).pack(
            side=tk.RIGHT, padx=5
        )
        ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(
            side=tk.RIGHT
        )

        if self.listbox.size() > 0:
            self.listbox.selection_set(0)  # Seleziona il primo elemento
            self.listbox.focus_set()  # Imposta il focus sulla listbox

        self.protocol("WM_DELETE_WINDOW", self._on_cancel)
        # self.wait_window() # Rimosso, gestito dal chiamante

    def _populate_listbox(self, items: List[str]):
        # (Implementazione come prima)
        self.listbox.delete(0, tk.END)
        for item in items:
            self.listbox.insert(tk.END, item)
        if self.listbox.size() > 0:
            self.listbox.selection_set(0)  # Mantiene la selezione del primo elemento

    def _apply_filter_to_listbox(self, *args):
        # (Implementazione come prima)
        filter_text = self.filter_var.get().lower()
        if not filter_text:
            self._populate_listbox(self._original_functions_list)
        else:
            filtered_list = [
                f for f in self._original_functions_list if filter_text in f.lower()
            ]
            self._populate_listbox(filtered_list)

    def _on_ok(self, event: Optional[tk.Event] = None):  # Aggiunto tipo per event
        # (Implementazione come prima)
        selection = self.listbox.curselection()
        if selection:
            self.result = self.listbox.get(selection[0])
        self.destroy()

    def _on_cancel(self):
        # (Implementazione come prima)
        self.result = None
        self.destroy()
