
import tkinter as tk
from tkinter import ttk, messagebox
import os
import json
import sys
import subprocess
from datetime import datetime
from typing import TYPE_CHECKING, List, Dict, Any, Optional

# Importa i moduli necessari
from cpp_python_debug.core.timeline_analyzer import create_timeline_from_session
from cpp_python_debug.gui.timeline_window import TimelineWindow

if TYPE_CHECKING:
    from cpp_python_debug.core.config_manager import AppSettings

class DumpAnalysisTab(ttk.Frame):
    """
    A tab for analyzing and visualizing past debug dump runs.
    """
    def __init__(self, parent: ttk.Notebook, app_settings: "AppSettings", log_directory_path: str, **kwargs):
        super().__init__(parent, **kwargs)
        self.app_settings = app_settings
        self.parent_notebook = parent

        # Percorso della cartella principale dei dump di profilo
        self.profile_dumps_dir = os.path.join(
            log_directory_path, "profile_dumps"
        )

        self._create_widgets()
        self.load_dump_runs()

        # Ricarica i run quando la tab diventa visibile
        self.bind("<Visibility>", lambda event: self.load_dump_runs())

    def _create_widgets(self):
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        # Frame per la tabella dei run
        runs_frame = ttk.LabelFrame(self, text="Available Dump Runs", padding="10")
        runs_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
        runs_frame.columnconfigure(0, weight=1)
        runs_frame.rowconfigure(0, weight=1)

        # Tabella (Treeview)
        self.runs_tree = ttk.Treeview(
            runs_frame,
            columns=("profile_name", "start_time", "end_time", "status", "dumps"),
            show="headings",
            selectmode="browse"
        )
        self.runs_tree.grid(row=0, column=0, sticky="nsew")

        # Headings
        self.runs_tree.heading("profile_name", text="Profile Name")
        self.runs_tree.heading("start_time", text="Start Time")
        self.runs_tree.heading("end_time", text="End Time")
        self.runs_tree.heading("status", text="Status")
        self.runs_tree.heading("dumps", text="Dumps")

        # Column widths
        self.runs_tree.column("profile_name", width=150, stretch=True)
        self.runs_tree.column("start_time", width=160, stretch=False)
        self.runs_tree.column("end_time", width=160, stretch=False)
        self.runs_tree.column("status", width=150, stretch=True)
        self.runs_tree.column("dumps", width=60, stretch=False, anchor="center")

        # Scrollbar
        scrollbar = ttk.Scrollbar(runs_frame, orient="vertical", command=self.runs_tree.yview)
        self.runs_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.grid(row=0, column=1, sticky="ns")

        self.runs_tree.bind("<<TreeviewSelect>>", self._on_run_selected)

        # Frame per i bottoni
        button_frame = ttk.Frame(self)
        button_frame.grid(row=1, column=0, sticky="se", padx=10, pady=(0, 10))

        self.analyze_button = ttk.Button(
            button_frame, text="Analyze...", state=tk.DISABLED, command=self._on_analyze_clicked
        )
        self.analyze_button.pack(side=tk.RIGHT, padx=5)

        self.open_folder_button = ttk.Button(
            button_frame, text="Open Folder", state=tk.DISABLED, command=self._open_selected_run_folder
        )
        self.open_folder_button.pack(side=tk.RIGHT, padx=5)

        self.refresh_button = ttk.Button(
            button_frame, text="Refresh", command=self.load_dump_runs
        )
        self.refresh_button.pack(side=tk.RIGHT)

    def load_dump_runs(self):
        """Scans the dump directory, parses summary files, and populates the treeview."""
        # Clear existing items
        for item in self.runs_tree.get_children():
            self.runs_tree.delete(item)

        self.analyze_button.config(state=tk.DISABLED)
        self.open_folder_button.config(state=tk.DISABLED)

        if not os.path.isdir(self.profile_dumps_dir):
            return

        run_folders = sorted(
            [d for d in os.listdir(self.profile_dumps_dir) if os.path.isdir(os.path.join(self.profile_dumps_dir, d))],
            reverse=True # Ordina dal più recente al più vecchio
        )

        for folder_name in run_folders:
            run_path = os.path.join(self.profile_dumps_dir, folder_name)
            summary_file = self._find_summary_file(run_path)

            if not summary_file:
                continue

            try:
                with open(summary_file, 'r', encoding='utf-8') as f:
                    summary_data = json.load(f)
                
                profile_name = summary_data.get("profile_name", "N/A")
                start_time = self._format_timestamp(summary_data.get("start_time"))
                end_time = self._format_timestamp(summary_data.get("end_time"))
                
                # Determine accurate status
                base_status = summary_data.get("status", "Unknown")
                has_errors = False
                if any(action.get('status', 'Success') != 'CompletedThisHit' for action in summary_data.get('actions_summary', [])):
                    has_errors = True
                if not has_errors:
                    if any(file.get('status', 'Success') != 'Success' for file in summary_data.get('files_produced_detailed', [])):
                        has_errors = True
                
                status = base_status
                if has_errors and "Completed" in base_status:
                    status = "Completed with Errors"
                elif has_errors:
                    status = "Error"

                dump_count = len(summary_data.get("files_produced_detailed", []))

                # Inserisce i dati nella tabella, passando il path come valore nascosto
                self.runs_tree.insert(
                    "", 
                    tk.END, 
                    values=(profile_name, start_time, end_time, status, dump_count),
                    iid=run_path # Usa il path come ID univoco della riga
                )

            except (IOError, json.JSONDecodeError):
                continue # Salta le cartelle con sommari corrotti
        
        # Evidenzia il primo elemento (il più recente)
        children = self.runs_tree.get_children()
        if children:
            self.runs_tree.selection_set(children[0])
            self.runs_tree.focus(children[0])

    def _find_summary_file(self, dir_path: str) -> Optional[str]:
        for filename in os.listdir(dir_path):
            if 'summary' in filename and filename.endswith('.json'):
                return os.path.join(dir_path, filename)
        return None

    def _format_timestamp(self, ts_string: Optional[str]) -> str:
        if not ts_string:
            return "N/A"
        try:
            # Formato ISO 8601 con microsecondi
            dt_obj = datetime.fromisoformat(ts_string)
            return dt_obj.strftime('%Y-%m-%d %H:%M:%S')
        except (ValueError, TypeError):
            return ts_string # Ritorna la stringa originale se il parsing fallisce

    def _on_run_selected(self, event=None):
        """Enable the analyze button when a run is selected."""
        if self.runs_tree.selection():
            self.analyze_button.config(state=tk.NORMAL)
            self.open_folder_button.config(state=tk.NORMAL)
        else:
            self.analyze_button.config(state=tk.DISABLED)
            self.open_folder_button.config(state=tk.DISABLED)

    def _on_analyze_clicked(self):
        """Handles the click of the 'Analyze' button."""
        selection = self.runs_tree.selection()
        if not selection:
            return

        session_path = selection[0]
        summary_file = self._find_summary_file(session_path)
        if not summary_file:
            messagebox.showerror("Error", "Could not find summary file for the selected run.", parent=self)
            return

        try:
            with open(summary_file, 'r', encoding='utf-8') as f:
                summary_data = json.load(f)
        except (IOError, json.JSONDecodeError) as e:
            messagebox.showerror("Error", f"Could not read or parse summary file:\n{e}", parent=self)
            return

        try:
            self.parent_notebook.config(cursor="watch")
            self.update_idletasks()

            timeline_df = create_timeline_from_session(session_path)

            self.parent_notebook.config(cursor="")

            if timeline_df.empty:
                messagebox.showinfo("Analysis Complete", "No valid dump data found in the selected run.", parent=self)
                return

            # Open the analysis window, now passing the full summary data
            TimelineWindow(
                self,
                timeline_df=timeline_df,
                summary_data=summary_data
            )

        except Exception as e:
            self.parent_notebook.config(cursor="")
            messagebox.showerror("Analysis Error", f"An unexpected error occurred during analysis:\n{e}", parent=self)

    def _open_selected_run_folder(self):
        """Opens the selected run's directory in the system's file explorer."""
        selection = self.runs_tree.selection()
        if not selection:
            return

        folder_path = selection[0]  # The IID of the row is the session path

        try:
            if os.path.isdir(folder_path):
                if sys.platform == "win32":
                    os.startfile(os.path.normpath(folder_path))
                elif sys.platform == "darwin":
                    subprocess.run(["open", folder_path], check=True)
                else:
                    subprocess.run(["xdg-open", folder_path], check=True)
            else:
                messagebox.showwarning("Directory Not Found", f"The directory no longer exists:\n{folder_path}", parent=self)
        except Exception as e:
            messagebox.showerror("Error Opening Folder", f"Could not open the folder:\n{e}", parent=self)
