"""
PanelStart module: Provides the PanelStart class for initializing and managing the retrieval process in the GUI.

This module handles parameter validation, database operations, and process management for the retrieval workflow.
"""
import os
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from tkinter import messagebox
import re

import numpy as np
import pandas as pd
# Removed unused import: from astroquery.utils.schema import priority

from GUIBRUSHR.GUI.DataInterface.DBSQLite3 import DBSQLite3
from GUIBRUSHR.GUI.WIDGET.MyButton import MyButton
from GUIBRUSHR.GUI.LAYOUT.MyPanel import MyPanel
from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables
from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import create_path_night

class PanelStart(MyPanel):
    """
    Panel for starting the retrieval process in the GUI.

    Handles initialization and execution of the retrieval process, including parameter validation,
    database operations, and process management.
    """

    def __init__(
        self,
        parent,
        row: int,
        column: int,
        color: str,
        path_main: str,
        path_default: str,
        **kwargs
    ):
        """
        Initialize the PanelStart class.

        Args:
            parent: Parent widget
            row (int): Row position in the grid
            column (int): Column position in the grid
            color (str): Background color
            path_main (str): Path to the main script
            path_default (str): Path to default configuration
            **kwargs: Additional keyword arguments
        """
        # Initialize instance variables
        self.total_instruments_HR = 0
        self.total_instruments_LR = 0
        self.total_nights = 0
        self.path_target = ''
        self.rad_mode = ''
        self.tell_rm_method = ''
        self.chemcat_bool = False
        self.sigma_priors_DB = ''
        self.molecs_equilibirum_DB = ''
        self.fixed_params_DB = ''
        self.bestpars_params_DB = ''
        self.retrieval_model = ''
        self.order_selection = ''
        self.path_default = path_default

        # Initialize parent class
        super().__init__(parent, color, row, column, **kwargs)

        # Set up paths and data structures
        self.path_main = path_main
        self.path_df_general_info = ''
        self.path_df_parameters = ''
        self.nights_and_instruments_db = ''
        self.resolution = ''
        self.ecc_opi = ''
        self.scattering = ''
        self.t_p_profile = ''
        self.chemistry = ''
        self.target_name = ''
        self.dt_string = ''
        self.path_results = ''
        self.string_instruments = ''

        # Process tracking
        self.id_process = 0
        self.list_pid = dict()
        self.list_file = dict()
        self.parent = parent

        # Data frames
        self.df_general_info = None
        self.df_parameter = None
        self.params_best_pars = None

        # Create start button
        self.button_start = MyButton(
            self, 0, 1,
            'START RETRIEVAL WITH THIS CONFIGURATION',
            '#54aedb',
            self.start_retrieval,
            color_panel=color
        )
        self.global_exc = 1

    def divide_instrument(self, inst_str: str, resolution: str) -> tuple[str, bool, str]:
        """
        Divide instrument string into components and validate paths.

        Args:
            inst_str (str): String containing instrument information
            resolution (str): Resolution type ('HR' or 'LR')

        Returns:
            tuple: (processed instrument string, error flag, error message)
        """
        # Replace separators for consistent parsing
        inst_str = inst_str.replace(",", "&")
        inst_str = inst_str.replace(";", "|")
        instr_list = inst_str.split("|")
        message = ""
        error = False
        for elem in instr_list:
            instr_parts = elem.split("&")
            if len(instr_parts) > 4:  # meaning HR
                self.total_instruments_HR += 1
                self.nights_and_instruments_db += (instr_parts[0] + "=" + instr_parts[4].replace("_", ",") + ";")
                self.total_nights += len(instr_parts[4].split("_"))
            else:
                self.nights_and_instruments_db += instr_parts[0] + ";"
                self.total_instruments_LR += 1
            self.string_instruments += instr_parts[0] + "_"
            if resolution == "HR":
                path_night = create_path_night(self.path_target, self.target_name, self.rad_mode, instr_parts[0])
                path_sub = instr_parts[0] + "&" + path_night if "H" in self.resolution else ""
                for night in instr_parts[4].split("_"):
                    path_order = Path(path_night, night, "orders_selection", self.order_selection)
                    # TODO Remove the mv in future updates
                    if not path_order.exists():
                        path_order_tmp = Path(path_night, night, self.order_selection)
                        if not path_order_tmp.exists():
                            error = True
                            message += f" - Order selection {self.order_selection} does not exist for path \n  {path_order_tmp} or {path_order}\n"
                            continue
                        path_order_without_sel = Path(path_night, night, "orders_selection")
                        os.system(f"mkdir -p {path_order_without_sel}")
                        os.system(f"mv {path_order_tmp} {path_order_without_sel}")
            else:
                path_sub = (instr_parts[0] +
                            "&" +
                            str(Path(
                                self.path_target, self.target_name, "LR_Instruments", self.rad_mode, instr_parts[0]))
                            ) if "L" in self.resolution else ""
            inst_str = re.sub(r"\b%s\b" % instr_parts[0], path_sub, inst_str)
            # inst_str = inst_str.replace(instr_parts[0] + "&", path_sub + "&")
        return inst_str, error, message


    def create_df_general_inf(self) -> tuple[bool, str]:
        """
        Create and validate the general information DataFrame for the retrieval process.

        Returns:
            tuple: (error flag, error message)
        """
        self.df_general_info = pd.DataFrame(columns=ConstantVariables.COLUMN_DF_GENERAL_INFO)
        self.path_target = self.parent.frame_input.frame_path.path_folder_targets.get_text()
        self.target_name = self.parent.panel_info.dropdown_target.get_value()
        self.resolution = self.parent.panel_info.dropdown_resolution.get_value()
        self.rad_mode = self.parent.panel_info.dropdown_rad_mode.get_value()
        self.order_selection = self.parent.panel_info.textfield_order_selection.get_text()
        instruments_hr = self.parent.panel_info.textfield_instrument_hr.get_text()
        instruments_lr = self.parent.panel_info.textfield_instrument_lr.get_text()
        message_hr = ""
        message_lr = ""
        error_hr = False
        error_lr = False
        self.string_instruments = ""
        self.nights_and_instruments_db = ""
        self.total_nights = 0
        self.total_instruments_HR = 0
        self.total_instruments_LR = 0
        if instruments_hr != "" and "H" in self.resolution:
            instruments_hr_list, error_hr, message_hr = self.divide_instrument(instruments_hr, "HR")
        else:
            instruments_hr_list = "Nothing"
        if instruments_lr != "" and "L" in self.resolution:
            instruments_lr_list, error_lr, message_lr = self.divide_instrument(instruments_lr, "LR")
        else:
            instruments_lr_list = "Nothing"

        # Retrieve mask phase information
        mask_phase_enabled = self.parent.panel_info.checkbox_mask_phase.get_value()
        mask_night_str = self.parent.panel_info.textfield_mask_night.get_text()
        min_phase = self.parent.panel_info.entry_min_phase.get_value()
        max_phase = self.parent.panel_info.entry_max_phase.get_value()
        which_mask = self.parent.panel_info.dropdown_mask_phase.get_value()

        # Validate mask night elements count if mask is enabled
        error_mask = False
        message_mask = ""
        if mask_phase_enabled:
            mask_elements = [elem.strip() for elem in mask_night_str.split(",") if elem.strip() != ""]
            if len(mask_elements) != self.total_nights:
                error_mask = True
                message_mask = (f"Mask night count mismatch: {len(mask_elements)} mask elements "
                               f"but {self.total_nights} total nights selected\n")

        self.string_instruments = self.string_instruments[:-1]
        now = datetime.now()
        self.dt_string = now.strftime("%Y_%m_%dT%H_%M_%S")
        self.path_results = str(Path(self.path_target, self.target_name, ConstantVariables.RESULTS_FOLDER, self.dt_string)) + os.sep
        # Use os.makedirs for directory creation
        os.makedirs(self.path_results, exist_ok=True)
        self.chemistry = self.parent.panel_info.dropdown_chemistry.get_value()
        self.chemcat_bool = self.chemistry == "Equilibrium"
        self.t_p_profile = self.parent.panel_info.dropdown_t_p_profile.get_value()
        self.scattering = self.parent.panel_info.check_scattering.get_value()
        self.ecc_opi = self.parent.panel_info.check_ecc_opi.get_value()
        self.tell_rm_method = self.parent.panel_info.dropdown_tell_rm_method.get_value()
        # error condition
        message = message_hr + message_lr + message_mask
        error = error_hr or error_lr or error_mask
        if "H" in self.resolution and (instruments_hr == "None" or instruments_hr == ""):
            error = True
            message += "No HR instrument selected \n"
        if "L" in self.resolution and (instruments_lr == "None" or instruments_lr == ""):
            error = True
            message += "No LR instrument selected \n"
        self.retrieval_model = self.parent.panel_info.dropdown_retrieval_mode.get_value()
        # Populate the DataFrame with general info
        target = self.parent.panel_info.target_information
        panel_info = self.parent.panel_info
        panel_param = self.parent.panel_param
        rows = [
            ["path_petitRadTrans", self.parent.frame_input.frame_path.path_petitRADTRANS.get_text()],
            ["path_results", self.path_results],
            ["target_name", self.target_name],
            ["target_radius", target.radius],
            ["target_mass", target.mass],
            ["target_gravity", target.gravity],
            ["target_t_eq", target.equilibrium_temperature],
            ["target_stellar_radius", target.stellar_radius],
            ["target_stellar_mass", target.stellar_mass],
            ["target_stellar_teff", target.stellar_effective_temperature],
            ["target_p0", target.pressure_at_layer_0],
            ["target_hjd0", target.hjd0],
            ["target_period", target.period],
            ["target_vsys", target.systemic_velocity],
            ["target_limph_t14", target.limit_phase_t14],
            ["target_limph_t12", target.limit_phase_t12],
            ["target_limph_t23", target.limit_phase_t23],
            ["target_t14", target.t14_hours],
            ["target_t12", target.t12_hours],
            ["target_t23", target.t23_hours],
            ["target_kp", target.kp],
            ["target_ks", target.ks],
            ["target_ecc", target.eccentricity],
            ["target_opi", target.argument_of_periastron],
            ["target_radius", target.radius],
            ["target_a_Rs_ratio", target.a_Rs_ratio],
            ["target_projected_obliquity", target.projected_obliquity],
            ["target_inclination", target.inclination],
            ["target_v_sini", target.v_sini],
            ["night_sim", panel_info.check_nights_sim.get_value()],
            ["scattering", self.scattering],
            ["ecc_opi", self.ecc_opi],
            ["maxsteps", panel_info.entry_maxsteps.get_value()],
            ["seed", panel_info.entry_seed.get_value()],
            ["chemistry", self.chemistry],
            ["chemcat_bool", self.chemcat_bool],
            ["t_p_profile", self.t_p_profile],
            ["continum_opacity", panel_info.textfield_continuum_opacity.get_text()],
            ["order_sel", self.order_selection],
            ["tell_rm_method", self.tell_rm_method],
            ["model_reprocessing", panel_info.dropdown_model_rep.get_value()],
            ["rad_mode", self.rad_mode],
            ["resolution", self.resolution],
            ["lbl_hr", panel_param.entry_lbl_sampling_hr.get_value()],
            ["lbl_lr", panel_param.entry_lbl_sampling_lr.get_value()],
            ["instruments_hr_list", instruments_hr_list],
            ["instruments_lr_list", instruments_lr_list],
            ["retrieval_model", self.retrieval_model],
            ["temp_min", panel_info.entry_temp_min.get_value()],
            ["temp_max", panel_info.entry_temp_max.get_value()],
            ["temp_step", panel_info.entry_temp_step.get_value()],
            ["layers", panel_info.entry_pressure_layers.get_value()],
            ["range_min_pressures", panel_info.entry_min_pressure.get_value()],
            ["range_max_pressures", panel_info.entry_max_pressure.get_value()],
            ["comments", panel_info.textfield_comment.get_text()],
            ["rv_sampling", panel_param.entry_rv_sampling.get_value()],
            ["stellar_spectrum_type_hr", panel_info.dropdown_stellar_spectrum_hr.get_value()],
            ["stellar_spectrum_type_lr", panel_info.dropdown_stellar_spectrum_lr.get_value()],
            ["stellar_spectrum_type_lr_mode", panel_info.dropdown_stellar_spectrum_lr_method.get_value()],
            ["multiplier_chains", panel_info.entry_multiplier_chains.get_value()],
            ["multiplier_cores", panel_info.entry_multiplier_cores.get_value()],
            ["use_pool", panel_info.checkbox_use_pool.get_value()],
            ["use_parallel_init", panel_info.checkbox_use_parallel_init.get_value()],
            ["standardize_PCA", panel_info.dropdown_standardize_pca.get_value()],
            ["use_hr_linelist_for_lr", panel_info.checkbox_use_hr_linelist_for_lr.get_value()],
            ["mask_phase_enabled", mask_phase_enabled],
            ["mask_night", mask_night_str],
            ["min_phase", min_phase],
            ["max_phase", max_phase],
            ["which_mask", which_mask],
        ]
        self.df_general_info = pd.DataFrame(rows, columns=["Variable", "Value"])
        #
        self.path_df_general_info = str(Path(self.path_results, "df_general_info.csv"))
        self.df_general_info.to_csv(self.path_df_general_info)
        return error, message

    def create_df_parameters(self) -> tuple[bool, str]:
        """
        Create and validate the parameters DataFrame for the retrieval process.

        This function processes parameters from the GUI, including non-molecular parameters,
        molecular parameters, and condensed molecular parameters if applicable. It populates
        the DataFrame with parameter details and prepares database strings for best parameters,
        fixed parameters, molecular equilibrium, and sigma priors.

        Returns:
            tuple: (error flag, error message)
        """
        self.params_best_pars = []
        self.bestpars_params_DB = ""
        self.fixed_params_DB = ""
        self.molecs_equilibirum_DB = ""
        self.sigma_priors_DB = ""
        # Initialize DataFrame for parameters
        self.df_parameter = pd.DataFrame(columns=ConstantVariables.COLUMN_DF_PARAMETERS)
        for i, elem in enumerate(ConstantVariables.COLUMN_DF_PARAMETERS):
            self.df_parameter[elem] = self.df_parameter[elem].astype(ConstantVariables.TYPES_DF_PARAMETERS[i])

        # Process non-molecular parameters
        for parameter in self.parent.panel_param.list_params_widget:
            present_var = parameter.checkbox_presence.get_value()
            if present_var and parameter.input_value.entry['state'] == "normal":
                name, isotope = parameter.get_name()
                opacity_name_low_resolution = None
                value = parameter.input_value.get_value()
                in_bestpars_var = parameter.checkbox_bestpars.get_value()
                chemical_formula = parameter.chemical_formula
                molec_var = parameter.is_molec
                mass = float("nan") if not molec_var else parameter.get_mass_value()
                scale = float(parameter.input_scale.get_value())
                range_min = float(parameter.input_range_min.get_value())
                range_max = float(parameter.input_range_max.get_value())
                rayleigh_var = parameter.get_rayleigh_value()
                constant_vmr = parameter.get_constant_vmr_value()
                sigma_prior = parameter.input_sigma_prior.get_value()

                # Handle multiple values for certain parameters
                if name in ConstantVariables.LIST_MULTIPLE_PARAM:
                    if name in ConstantVariables.LIST_DERIVATIVE_PARAMS:
                        total_params = self.total_nights - self.total_instruments_HR
                    elif name in ConstantVariables.LIST_MOLECULAR_PARAMS:
                        total_params = len(value)
                    elif name in ConstantVariables.LIST_INSTRUMENTAL_PARAMS:
                        total_params = self.total_instruments_LR - 1
                    else:
                        total_params = self.total_nights
                    value = np.ones(total_params) * float(value)
                    sigma_prior = np.ones(total_params) * float(sigma_prior)
                    for multi_cont in range(total_params):
                        multi_param_name = name + str(multi_cont)
                        multi_param_value = value[multi_cont]
                        multi_param_sigma_prior = sigma_prior[multi_cont]
                        if in_bestpars_var:
                            self.params_best_pars.append(multi_param_name)
                            self.bestpars_params_DB += multi_param_name + "=" + str(multi_param_value) + ","
                        else:
                            self.fixed_params_DB += multi_param_name + "=" + str(multi_param_value) + ","
                        if multi_param_sigma_prior < 0:
                            multi_param_sigma_prior = float("nan")
                        self.add_to_df_parameters(
                            multi_param_name, present_var, molec_var, float(multi_param_value), scale, range_min, range_max,
                            rayleigh_var, in_bestpars_var, mass, multi_param_sigma_prior,
                            chemical_formula, constant_vmr, isotope, opacity_name_low_resolution
                        )
                else:
                    if in_bestpars_var:
                        self.params_best_pars.append(name)
                        self.bestpars_params_DB += name + "=" + str(value) + ","
                    else:
                        self.fixed_params_DB += name + "=" + str(value) + ","
                    value = float(value)
                    sigma_prior = float(sigma_prior)
                    if sigma_prior < 0:
                        sigma_prior = float("nan")
                    else:
                        self.sigma_priors_DB += (name + "=" + str(sigma_prior) + ",")
                    self.add_to_df_parameters(
                        name, present_var, molec_var, value, scale, range_min, range_max,
                        rayleigh_var, in_bestpars_var, mass, sigma_prior, chemical_formula,
                        constant_vmr, isotope, opacity_name_low_resolution
                    )

        # Process molecular parameters
        molec_list = self.parent.panel_param.textfield_molecs.get_text()
        error = False
        message = ""
        if molec_list == "":
            error = True
            message += "No molec selected \n"
        else:
            molec_list = molec_list.split("\n")
            molec_list = sorted(molec_list)
            for elem in molec_list:
                string_comp = elem.split(",")
                chemical_formula = string_comp[0]
                isotope = string_comp[1]
                opacity_name_high_resolution = string_comp[2]
                opacity_name_low_resolution = string_comp[3]
                present_var = int(string_comp[4])
                molec_var = int(string_comp[5])
                value = float(string_comp[6])
                scale = float(string_comp[7])
                range_min = float(string_comp[8])
                range_max = float(string_comp[9])
                rayleigh_var = int(string_comp[10])
                in_bestpars_var = int(string_comp[11])
                mass = float(string_comp[12])
                sigma_prior = float(string_comp[13])
                constant_vmr = int(string_comp[14])
                self.molecs_equilibirum_DB += chemical_formula + "=" + opacity_name_high_resolution + ","
                if self.chemistry == ConstantVariables.LIST_CHEMISTRY_TABLE[1]:
                    if in_bestpars_var:
                        self.params_best_pars.append(opacity_name_high_resolution)
                        self.bestpars_params_DB += opacity_name_high_resolution + "=" + str(value) + ","
                    else:
                        self.fixed_params_DB += opacity_name_high_resolution + "=" + str(value) + ","
                else:
                    in_bestpars_var = 0
                if sigma_prior < 0:
                    sigma_prior = float("nan")
                else:
                    self.sigma_priors_DB += (opacity_name_high_resolution + "=" + str(sigma_prior) + ",")
                self.add_to_df_parameters(
                    opacity_name_high_resolution, present_var, molec_var, value, scale, range_min, range_max,
                    rayleigh_var, in_bestpars_var, mass, sigma_prior, chemical_formula,
                    constant_vmr, isotope, opacity_name_low_resolution
                )

        # Process hybrid chemistry elements if applicable
        if self.chemistry == ConstantVariables.LIST_CHEMISTRY_TABLE[2]:
            elements_list = self.parent.panel_param.textfield_hce.get_text()
            if elements_list == "":
                error = True
                message += "No elements selected for hybrid \n"
            else:
                elements_list = elements_list.split("\n")
                elements_list = sorted(elements_list)
                for elem in elements_list:
                    string_comp = elem.split(",")
                    name = string_comp[0]
                    present_var = int(string_comp[1])
                    molec_var = 2  # Special flag for hybrid chemistry elements
                    value = float(string_comp[3])
                    scale = float(string_comp[4])
                    range_min = float(string_comp[5])
                    range_max = float(string_comp[6])
                    rayleigh_var = int(string_comp[7])
                    in_bestpars_var = int(string_comp[8])
                    sigma_prior = float(string_comp[10])
                    chemical_formula = string_comp[11]
                    
                    # Add to best parameters or fixed parameters based on in_bestpars_var
                    if in_bestpars_var:
                        self.params_best_pars.append(name)
                        self.bestpars_params_DB += name + "=" + str(value) + ","
                    else:
                        self.fixed_params_DB += name + "=" + str(value) + ","
                    
                    # Set default values for hybrid chemistry elements
                    isotope = None
                    opacity_name_low_resolution = None
                    mass = float("nan")
                    if sigma_prior < 0:
                        sigma_prior = float("nan")
                    else:
                        self.sigma_priors_DB += (name + "=" + str(sigma_prior) + ",")
                    constant_vmr = False
                    
                    # Add the hybrid chemistry element to the parameters DataFrame
                    self.add_to_df_parameters(
                        name, present_var, molec_var, value, scale, range_min, range_max,
                        rayleigh_var, in_bestpars_var, mass, sigma_prior, chemical_formula,
                        constant_vmr, isotope, opacity_name_low_resolution
                    )

        # Process condensed molecular parameters if applicable
        if self.parent.panel_info.checkbox_include_condensed.get_value():
            condensed_molec_list = self.parent.panel_param.textfield_condensed_molecs.get_text()
            if condensed_molec_list == "":
                error = True
                message += "No condensed molec selected \n"
            else:
                condensed_molec_list = condensed_molec_list.split("\n")
                condensed_molec_list = sorted(condensed_molec_list)
                for elem in condensed_molec_list:
                    string_comp = elem.split(",")
                    isotope = None
                    opacity_name_low_resolution = None
                    name = string_comp[0]
                    present_var = int(string_comp[1])
                    molec_var = int(string_comp[2])
                    value = float(string_comp[3])
                    scale = float(string_comp[4])
                    range_min = float(string_comp[5])
                    range_max = float(string_comp[6])
                    rayleigh_var = int(string_comp[7])
                    in_bestpars_var = int(string_comp[8])
                    mass = float(string_comp[9])
                    sigma_prior = float(string_comp[10])
                    constant_vmr = int(string_comp[11])
                    if sigma_prior < 0:
                        sigma_prior = float("nan")
                    else:
                        self.sigma_priors_DB += name + "=" + str(sigma_prior)
                    chemical_formula = string_comp[12]
                    self.molecs_equilibirum_DB += ConstantVariables.ALL_CONDENSED_MOLEC[name] + "=" + name + ","
                    if self.chemistry == ConstantVariables.LIST_CHEMISTRY_TABLE[1]:
                        if in_bestpars_var:
                            self.params_best_pars.append(name)
                            self.bestpars_params_DB += name + "=" + str(value) + ","
                        else:
                            self.fixed_params_DB += name + "=" + str(value) + ","
                    else:
                        in_bestpars_var = 0
                    self.add_to_df_parameters(
                        name, present_var, molec_var, value, scale, range_min, range_max,
                        rayleigh_var, in_bestpars_var, mass, sigma_prior, chemical_formula,
                        constant_vmr, isotope, opacity_name_low_resolution
                    )

        # Finalize database strings
        self.molecs_equilibirum_DB = self.molecs_equilibirum_DB[:-1]
        self.fixed_params_DB = self.fixed_params_DB[:-1]
        self.bestpars_params_DB = self.bestpars_params_DB[:-1]
        self.sigma_priors_DB = self.sigma_priors_DB[:-1]
        self.path_df_parameters = str(Path(self.path_results, "df_parameters.csv"))
        if self.df_parameter['name'].duplicated().any():
            error = True
            message += "Duplicated elements in the parameter dataframe \n"
        self.df_parameter.to_csv(self.path_df_parameters)
        return error, message

    def add_to_df_parameters(
        self, name: str, present_var: int, molec_var: int, value: float, scale: float, range_min: float, range_max: float,
        rayleigh_var: int, in_bestpars_var: int, mass: float, sigma_prior: float, chemical_formula: str,
        constant_vmr: int, isotope: [str, None], opacity_name_low_resolution: [str, None] = None
    ) -> None:
        """
        Add a parameter to the parameters DataFrame.

        Args:
            name (str): Parameter name
            present_var (int): Whether the parameter is present
            molec_var (int): Whether the parameter is molecular
            value (float): Parameter value
            scale (float): Scale factor
            range_min (float): Minimum range
            range_max (float): Maximum range
            rayleigh_var (int): Rayleigh variable
            in_bestpars_var (int): Whether the parameter is in best parameters
            mass (float): Mass of the parameter
            sigma_prior (float): Sigma prior
            chemical_formula (str): Chemical formula
            constant_vmr (int): Constant VMR flag
            isotope (str): Isotope information
            opacity_name_low_resolution (str, optional): Opacity name for low resolution. Defaults to None.
        """
        arr = [
            name, present_var, molec_var, value, scale, range_min, range_max,
            rayleigh_var, in_bestpars_var, mass, sigma_prior, chemical_formula,
            constant_vmr, isotope, opacity_name_low_resolution
        ]
        new_row = pd.Series(arr, index=ConstantVariables.COLUMN_DF_PARAMETERS)
        self.df_parameter = pd.concat([self.df_parameter, pd.DataFrame([new_row])], ignore_index=True)

    def start_retrieval(self) -> None:
        """
        Start the retrieval process by validating parameters and initializing the database.

        This function creates and validates the general information and parameters DataFrames,
        prepares the output file, and inserts retrieval data into the database. It then starts
        the main retrieval process as a subprocess.
        """
        # Create and validate data frames
        error_df, message_df = self.create_df_general_inf()
        error_par, message_par = self.create_df_parameters()

        # Show error if validation fails
        if error_par or error_df:
            messagebox.showerror("Errors", message_df + message_par)
            return

        # Create output file
        path_file_output = str(Path(self.path_results, "output.txt"))
        with open(path_file_output, "w") as f:
            f.write("Output file created")

        # Prepare parameter names for database
        # param_name = ",".join(self.params_best_pars)

        # Start main process
        self.start_main(
            self.path_df_parameters,
            self.path_df_general_info,
            path_file_output,
            self.target_name,
            self.dt_string,
            resume="False",
            checkbox_taskset=self.parent.panel_info.checkbox_use_tasket.get_value(),
            end_core=self.parent.panel_info.entry_end_core.get_value(),
            priority_proc=self.parent.panel_info.entry_priority_process.get_value(),
            n_best_pars=len(self.params_best_pars),
            chains_multiplier=int(self.parent.panel_info.entry_multiplier_chains.get_value()),
            multiplier_cores=int(self.parent.panel_info.entry_multiplier_cores.get_value())
        )

    def start_main(
        self, path_df_parameters: str, path_df_general_info: str, path_file_output: str, target_name: str, dt_string: str, resume: str = "False",
        checkbox_taskset: bool = False, end_core: int = 255, priority_proc: int = 19, n_best_pars: int = 10, chains_multiplier: int = 2,
        multiplier_cores:int = 1
    ) -> None:
        """
        Start the main retrieval process as a subprocess.

        Args:
            path_df_parameters (str): Path to parameters DataFrame
            path_df_general_info (str): Path to general info DataFrame
            path_file_output (str): Path to output file
            target_name (str): Name of the target
            dt_string (str): Datetime string
            resume (str): Whether to resume a previous process
            checkbox_taskset (bool): Whether to use taskset
            end_core (int): End core for taskset
            priority_proc (int): Priority for the process
            n_best_pars (int): Number of best parameters
            chains_multiplier: Multiplier for the number of chains
            multiplier_cores: Multiplier for the number of cores
        """
        # Calculate configuration parameters
        nchains = n_best_pars * chains_multiplier
        n_cores = int(nchains / multiplier_cores)

        # Build configuration message
        config_message = f"Retrieval Configuration:\n\n"
        config_message += f"Number of chains: {nchains}\n"
        config_message += f"Number of cores: {n_cores}\n"
        config_message += f"Number of parameters: {n_best_pars}\n"

        if checkbox_taskset:
            start_core = int(end_core) - n_cores
            cores_range = f"{start_core}-{end_core}"
            config_message += f"Taskset enabled: YES\n"
            config_message += f"Core range: {cores_range}\n"
        else:
            config_message += f"Taskset enabled: NO\n"

        config_message += f"\nDo you want to start the retrieval with this configuration?"

        # Show confirmation dialog
        user_response = messagebox.askyesno("Confirm Retrieval Start", config_message)

        # If user clicks No, return without doing anything
        if not user_response:
            print("User cancelled retrieval start")
            return
        print("Order bestpars param:", self.params_best_pars)

        # Get username and initialize database
        username = os.environ.get("USER")
        DB = DBSQLite3(self.path_default)

        # Insert retrieval data into database
        DB.insert_retrieval_into_DB(
            self.dt_string, self.target_name, self.nights_and_instruments_db,
            self.chemistry, self.t_p_profile, self.scattering,
            self.ecc_opi, self.order_selection, self.retrieval_model,
            self.resolution, self.tell_rm_method, self.rad_mode,
            "_".join(sorted(self.string_instruments.split('_'))),
            username, self.bestpars_params_DB[:-1],
            self.fixed_params_DB, self.molecs_equilibirum_DB,
            self.sigma_priors_DB
        )
        DB.close_DB()

        # Start subprocess
        if checkbox_taskset:
            taskset = "taskset"
            arg_taskset_c = "-c"
            cores = f"{(int(end_core) - n_cores)}-{end_core}"
            print(cores)
            arg_nice = "nice"
            arg_taskset_n = "-n"
            process = subprocess.Popen(
                [
                    taskset,
                    arg_taskset_c,
                    cores,
                    arg_nice,
                    arg_taskset_n,
                    str(priority_proc),
                    sys.executable,
                    self.path_main,
                    path_df_parameters,
                    path_df_general_info,
                    path_file_output,
                    str(self.id_process),
                    str(self.path_default),
                    resume
                ],
                stderr=subprocess.STDOUT,
                universal_newlines=True,
                shell=False,
            )
        else:
            process = subprocess.Popen(
                [
                    sys.executable,
                    self.path_main,
                    path_df_parameters,
                    path_df_general_info,
                    path_file_output,
                    str(self.id_process),
                    str(self.path_default),
                    resume
                ],
                stderr=subprocess.STDOUT,
                universal_newlines=True,
                shell=False,
            )

        # Update process tracking
        self.id_process += 1
        self.list_pid[self.id_process] = process
        self.list_file[self.id_process] = path_file_output

        # Update output table
        self.parent.frame_input.parent.frame_output_table.panel_table.table.insert(
            parent="",
            index="end",
            iid=self.id_process,
            text="",
            values=(process.pid, target_name, dt_string, self.id_process, ""),
        )
