"""
Frame for converting high-resolution line-by-line opacities to correlated-k format.
This module handles the conversion of petitRADTRANS line-by-line opacity files
to lower resolution correlated-k format for faster radiative transfer calculations.
"""
import subprocess
import threading
from pathlib import Path
from tkinter import messagebox, filedialog

from GUIBRUSHR.GUI.LAYOUT.MyPanel import MyPanel
from GUIBRUSHR.GUI.WIDGET.MyButton import MyButton
from GUIBRUSHR.GUI.WIDGET.MyTextField import MyTextField
from GUIBRUSHR.General_Constants.Classes.HelpButton import HelpButton
from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables


class FrameConvertOpacity(MyPanel):
    """
    A panel for converting line-by-line opacities to correlated-k format.

    This class provides functionality to:
    - Select input high-resolution opacity file
    - Set target resolution for correlated-k
    - Launch conversion process in background
    - Monitor conversion progress
    """

    def __init__(self, parent, color, row, column, path_default, **kwargs):
        """Initialize the FrameConvertOpacity panel with all necessary components."""
        super().__init__(parent, color, row, column, **kwargs)

        # Store instance variables
        self.path_default = path_default
        self.color = color
        self.conversion_process = None
        self.is_converting = False

        # Initialize panels
        self._init_panels()
        # Initialize controls
        self._init_controls()

    def _init_panels(self):
        """Initialize the main panel columns."""
        self.main_panel = MyPanel(self, self.color, 0, 0)

    def _init_controls(self):
        """Initialize conversion controls."""
        # Help text dictionary
        help_text = {
            "Opacity HR Path": "Path to the input high-resolution line-by-line opacity file.\n"
                               "Format: petitRADTRANS HDF5 file (e.g., *__*.R1e6_*.xsec.petitRADTRANS.h5)\n"
                               "Click 'Browse' to select a file or paste the path directly.",

            "Output Resolution": "Target resolving power (R = λ/Δλ) for the correlated-k output.\n"
                                 "Typical values: 1000-100000\n"
                                 "Higher values = better accuracy but larger file size.\n"
                                 "Default: 40000\n"
                                 "Note: Must be lower than input resolution (~1e6).",

            "Conversion": "Launch the opacity conversion process in background.\n"
                          "The conversion may take several minutes depending on file size.\n"
                          "Output will be saved in the 'correlated_k' subdirectory.\n"
                          "Diagnostic plots will be generated automatically."
        }

        # Create help button
        self.help_button = HelpButton(
            self.main_panel, 0, 0,
            "Opacity Conversion Help",
            help_text,
            columnspan=1
        )
        self.help_button.button.grid(rowspan=4)

        # Opacity HR path text field (large, with browse button)
        self.textfield_opacity_path = MyTextField(
            self.main_panel, 0, 1,
            "",  # Initial empty text
            label_text="Opacity HR Path:",
            color=self.color,
            width=80,
            height=1,
            columnspan=2
        )

        # Browse button for selecting file
        self.button_browse = MyButton(
            self.main_panel, 0, 3,
            "Browse",
            bg="#4CAF50",
            command=self._browse_opacity_file,
            color_panel=self.color
        )

        # Output resolution text field
        self.textfield_resolution = MyTextField(
            self.main_panel, 1, 1,
            "40000",  # Default value
            label_text="Output Resolution:",
            color=self.color,
            width=15,
            height=1,
            columnspan=2
        )

        # Status text field (read-only display)
        self.textfield_status = MyTextField(
            self.main_panel, 2, 1,
            "Ready",
            label_text="Status:",
            color=self.color,
            width=80,
            height=2,
            columnspan=2
        )
        # Make status field read-only by disabling it
        self.textfield_status.text_widget.config(state='disabled')

        # Conversion button
        self.button_convert = MyButton(
            self.main_panel, 3, 1,
            "Start Conversion to Correlated-K",
            bg="#1e4fda",
            command=self._start_conversion,
            color_panel=self.color,
            columnspan=2
        )

        # Stop button (initially disabled)
        self.button_stop = MyButton(
            self.main_panel, 3, 3,
            "Stop",
            bg="#f44336",
            command=self._stop_conversion,
            color_panel=self.color
        )
        self.button_stop.button.config(state="disabled")

    def _browse_opacity_file(self):
        """Open file dialog to browse for opacity file."""
        initial_dir = Path(ConstantVariables.path_petitradtrans, "opacities", "lines", "line_by_line")

        filename = filedialog.askopenfilename(
            title="Select Line-by-Line Opacity File",
            initialdir=initial_dir,
            filetypes=[
                ("HDF5 files", "*.h5"),
                ("All files", "*.*")
            ]
        )

        if filename:
            self.textfield_opacity_path.insert_text(filename)

    def _update_status(self, status_text: str):
        """Update the status text field."""
        self.textfield_status.text_widget.config(state='normal')
        self.textfield_status.insert_text(status_text)
        self.textfield_status.text_widget.config(state='disabled')

    def _get_opacity_path(self) -> str:
        """Get opacity path from text field."""
        return self.textfield_opacity_path.text_widget.get("1.0", "end-1c").strip()

    def _get_resolution(self) -> str:
        """Get resolution from text field."""
        return self.textfield_resolution.text_widget.get("1.0", "end-1c").strip()

    def _validate_inputs(self) -> tuple[bool, str, int]:
        """
        Validate user inputs.

        Returns
        -------
        tuple[bool, str, int]
            (valid, opacity_path, resolution)
        """
        # Get opacity path
        opacity_path = self._get_opacity_path()
        if not opacity_path:
            messagebox.showerror("Error", "Please provide an opacity file path.")
            return False, "", 0

        # Check if file exists
        if not Path(opacity_path).exists():
            messagebox.showerror("Error", f"Opacity file not found:\n{opacity_path}")
            return False, "", 0

        # Check if it's an HDF5 file
        if not opacity_path.endswith('.h5'):
            messagebox.showerror("Error", "Opacity file must be an HDF5 file (.h5)")
            return False, "", 0

        # Get resolution
        resolution_str = self._get_resolution()
        if not resolution_str:
            messagebox.showerror("Error", "Please provide an output resolution.")
            return False, "", 0

        try:
            resolution = int(resolution_str)
            if resolution <= 0:
                raise ValueError("Resolution must be positive")
            if resolution > 1e6:
                messagebox.showwarning(
                    "Warning",
                    f"Resolution {resolution} is very high.\n"
                    f"This may result in large file sizes and long computation times."
                )
        except ValueError:
            messagebox.showerror("Error", "Resolution must be a positive integer.")
            return False, "", 0

        return True, opacity_path, resolution

    def _start_conversion(self):
        """Start the opacity conversion process in background."""
        if self.is_converting:
            messagebox.showwarning("Warning", "Conversion already in progress.")
            return

        # Validate inputs
        valid, opacity_path, resolution = self._validate_inputs()
        if not valid:
            return

        # Confirm with user
        response = messagebox.askyesno(
            "Start Conversion",
            f"Start conversion of:\n{opacity_path}\n\n"
            f"Target resolution: R = {resolution:,}\n\n"
            f"This process will run in the background and may take several minutes.\n"
            f"Continue?"
        )

        if not response:
            return

        # Update UI
        self.is_converting = True
        self.button_convert.button.config(state="disabled")
        self.button_stop.button.config(state="normal")
        self._update_status("Converting... (this may take several minutes)")

        # Launch conversion in separate thread
        conversion_thread = threading.Thread(
            target=self._run_conversion,
            args=(opacity_path, resolution),
            daemon=True
        )
        conversion_thread.start()

    def _run_conversion(self, opacity_path: str, resolution: int):
        """
        Run the conversion process in background.

        Parameters
        ----------
        opacity_path : str
            Path to input opacity file
        resolution : int
            Target resolution
        """
        try:
            # Path to conversion script
            script_path = Path(self.path_default, "GUIBRUSHR", "GUI", "Input_Output_Panels", "Input_Panels", "TabPanels", "FrameDBInteractions", "generate_correlated_k_from_lbl.py")

            if not script_path.exists():
                raise FileNotFoundError(f"Conversion script not found: {script_path}")

            # Build command
            cmd = [
                "python",
                str(script_path),
                "--input-file", opacity_path,
                "--resolution", str(resolution)
            ]

            # Run process
            self.conversion_process = subprocess.Popen(
                cmd,
                text=True,
                cwd=str(Path(self.path_default).parent)
            )

            # Wait for completion
            stdout, stderr = self.conversion_process.communicate()

            # Check result
            if self.conversion_process.returncode == 0:
                self._conversion_success(stdout)
            else:
                self._conversion_error(stderr)

        except Exception as e:
            self._conversion_error(str(e))
        finally:
            self.conversion_process = None

    def _conversion_success(self, stdout: str):
        """Handle successful conversion."""
        self.is_converting = False

        # Update UI (must be done in main thread)
        self.after(0, lambda: self.button_convert.button.config(state="normal"))
        self.after(0, lambda: self.button_stop.button.config(state="disabled"))
        self.after(0, lambda: self._update_status("Conversion completed successfully!"))

        # Show success message
        self.after(0, lambda: messagebox.showinfo(
            "Success",
            "Opacity conversion completed successfully!\n\n"
            "Output file saved in the same directory as input,\n"
            "in the 'correlated_k' subdirectory."
        ))

    def _conversion_error(self, error_msg: str):
        """Handle conversion error."""
        self.is_converting = False

        # Update UI
        self.after(0, lambda: self.button_convert.button.config(state="normal"))
        self.after(0, lambda: self.button_stop.button.config(state="disabled"))
        self.after(0, lambda: self._update_status("Conversion failed"))

        # Show error message (truncate if too long)
        error_display = error_msg if len(error_msg) < 500 else error_msg[:500] + "\n..."

        self.after(0, lambda: messagebox.showerror(
            "Conversion Error",
            f"Opacity conversion failed:\n\n{error_display}"
        ))

    def _stop_conversion(self):
        """Stop the running conversion process."""
        if not self.is_converting or self.conversion_process is None:
            return

        response = messagebox.askyesno(
            "Stop Conversion",
            "Are you sure you want to stop the conversion?\n"
            "This will terminate the process immediately."
        )

        if response:
            try:
                self.conversion_process.terminate()
                self.conversion_process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                self.conversion_process.kill()

            self.is_converting = False
            self.conversion_process = None

            self.button_convert.button.config(state="normal")
            self.button_stop.button.config(state="disabled")
            self._update_status("Conversion stopped by user")

            messagebox.showinfo("Stopped", "Conversion process stopped.")

    def cleanup(self):
        """Clean up resources when panel is destroyed."""
        if self.is_converting and self.conversion_process:
            self.conversion_process.terminate()
