from numpy import array
from pathlib import Path
from typing import List, Dict
import yaml

from GUIBRUSHR.General_Constants.Classes.Target import Target
from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables
from GUIBRUSHR.GUI.DataInterface.DBSQLite3 import DBSQLite3
from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import create_path_night


def get_target_list(path_targets_folder: Path) -> array:
    """Get a list of all target directories in the specified folder.
    
    Args:
        path_targets_folder (Path): Path to the targets folder
        
    Returns:
        array: Array of target names, or ["None"] if no targets found
    """
    target_list = [d.name for d in Path(path_targets_folder).iterdir() if d.is_dir()]
    return array(sorted(target_list if target_list else ["None"]))


def get_target_info(path_targets_folder: Path, target: str, rad_mode: str = "Transmission") -> Target:
    """Read and parse target information from YAML file.
    
    Args:
        path_targets_folder (Path): Path to the targets folder
        target (str): Target identifier
        rad_mode (str, optional): Radiation mode. Defaults to "Transmission"
        
    Returns:
        Target: Target object containing parsed information
    """
    path_file = str(Path(path_targets_folder, target, "info.yaml")) if target != "None" else None
    return Target(path_file, rad_mode)


def get_list_cross_corr_models(path_targets_folder: Path, target: str, rad_mode: str) -> Dict[str, Path]:
    """Get dictionary of cross-correlation models for a target.
    
    Args:
        path_targets_folder (Path): Path to the targets folder
        target (str): Target identifier
        rad_mode (str): Radiation mode
        
    Returns:
        Dict[str, Path]: Dictionary mapping model names to their paths,
            or {"None": "None"} if no models are found
    """
    path_folder = str(Path(path_targets_folder, target, "Models", rad_mode))
    dict_name = {}
    
    if Path(path_folder).exists():
        dict_name = {d.name: d.absolute() for d in Path(path_folder).iterdir() if d.is_dir()}
    
    return dict_name if dict_name else {"None": "None"}


def get_target_instruments(path_targets_folder: Path, target: str, resolution: str, rad_mode: str, calling_file_name: str) -> List[str]:
    """Get list of instruments available for a target at specified resolution.
    
    Args:
        path_targets_folder (Path): Path to the targets folder
        target (str): Target identifier
        resolution (str): Resolution type (e.g., "HR" or "LR")
        rad_mode (str): Radiation mode
        calling_file_name (str): Name of the calling function (for logging purposes)

    Returns:
        List[str]: List of instrument names, or ["None"] if no instruments found
    """
    path_folder = str(Path(path_targets_folder, target, f"{resolution}_Instruments", rad_mode))
    instruments_local = []
    
    if Path(path_folder).exists():
        instruments_local = [d.name for d in Path(path_folder).iterdir() if d.is_dir()]

    DB = DBSQLite3(ConstantVariables.path_default)
    instruments_DB, _, _, _, _ = DB.get_instruments()
    DB.close_DB()
    
    if instruments_local:
        instruments_result = list(set(instruments_local).intersection(set(instruments_DB)))
        if not instruments_result:
            print(f"No Instruments Found at {resolution} Resolution for {target} in the DB")
            return ["None"]
    else:
        print(f"File {calling_file_name}: No Instruments Found at {resolution} Resolution for {target} in local folders")
        return ["None"]
    
    return sorted(instruments_result)


def get_target_nights(path_targets: Path, target: str, rad_mode: str, instrument: str, simulated) -> str:
    """Get list of observation nights for a target and instrument.

    Args:
        path_targets (Path): Path to the targets folder
        target (str): Target identifier
        rad_mode (str): Radiation mode
        instrument (str): Instrument identifier
        simulated (bool): Whether to include simulated nights

    Returns:
        str: Comma-separated list of nights, or "None" if no nights found
    """
    nights = "None"
    try:
        with DBSQLite3(ConstantVariables.path_default) as DB:
            instrument_obj = DB.get_instrument_by_key(instrument)

            if instrument_obj is not None:
                path_instrument = create_path_night(path_targets, target, rad_mode, instrument_obj.name)
                night_result = [d.name for d in Path(path_instrument).iterdir() if d.is_dir()]

                if night_result:
                    nights = ",".join(
                        elem for elem in sorted(night_result)
                        if ("sim" not in elem and "syn" not in elem and not simulated) or (("sim" in elem or "syn" in elem) and simulated)
                    )
    except Exception as e:
        print(f"WARNING in tell rem: {e}")

    return nights


def insert_target_catalog(
        path_target: Path,
        target_id: str,
        radius,
        mass,
        gravity,
        t_eq,
        stellar_radius,
        stellar_mass,
        p0,
        hjd0,
        period,
        vsys,
        limph_T14,
        limph_T12,
        limph_T23,
        kp,
        ks,
        ecc,
        t14,
        t12,
        t23,
        opi=1.57,
        stellar_teff=0,
        ra=0,
        dec=0,
        a_Rs_ratio=0,
        projected_obliquity=0,
        inclination=0,
        v_sini=0
) -> None:
    """Create a new target catalog entry with associated directories.

    Args:
        path_target (Path): Base path for targets
        target_id (str): Target identifier
        radius : Radius in Jupiter radii
        mass : Mass in Jupiter masses
        gravity : Surface gravity in cm/s²
        t_eq : Equilibrium temperature in Kelvin
        stellar_radius : Stellar radius in solar radii
        stellar_mass : Stellar mass in solar masses
        p0 : Reference pressure in log10(bar)
        hjd0 : Reference HJD in days
        period : Orbital period in days
        vsys : Systemic velocity in km/s
        limph_T14 : Phase limit in hours for T14 transit
        limph_T12 : Phase limit in hours for T12 transit
        limph_T23 : Phase limit in hours for T23 transit
        kp : Planet Semi-amplitude in km/s
        ks : Star Semi-amplitude in km/s
        ecc : Eccentricity
        t14 : Total transit duration in hours for T14 transit
        t12 : Total transit duration in hours for T12 transit
        t23 : Total transit duration in hours for T23 transit
        opi : Periastron argument in radians. Defaults to 1.57
        stellar_teff : Stellar effective temperature in Kelvin. Defaults to 0
        ra: Right ascension in degrees. Defaults to 0
        dec: Declination in degrees. Defaults to 0
        a_Rs_ratio: Semi-major axis to stellar radius ratio. Defaults to 0
        projected_obliquity: Projected stellar obliquity in degrees. Defaults to 0
        inclination: Orbital inclination in degrees. Defaults to 0
        v_sini: Projected stellar rotational velocity in km/s. Defaults to 0
    """
    yaml_dict = {
        'name': target_id,
        'radius_jup': float(radius),
        'mass_jup': float(mass),
        'gravity_cm_s2': float(gravity),
        't_eq_K': float(t_eq),
        'stellar_radius_sun': float(stellar_radius),
        'stellar_mass_sun': float(stellar_mass),
        'stellar_teff_K': float(stellar_teff),
        'p0_log10_bar': float(p0),
        'hjd0_days': float(hjd0),
        'period_days': float(period),
        't14_hours': float(t14),
        't12_hours': float(t12),
        't23_hours': float(t23),
        'Limit_Phase_T14': float(limph_T14),
        'Limit_Phase_T12': float(limph_T12),
        'Limit_Phase_T23': float(limph_T23),
        'kp_km_s': float(kp),
        'ks_km_s': float(ks),
        'v_system_km_s': float(vsys),
        'ecc': float(ecc),
        'periastron_argument': float(opi),
        'ra': float(ra),
        'dec': float(dec),
        'a_Rs_ratio': float(a_Rs_ratio),
        'projected_obliquity_deg': float(projected_obliquity),
        'inclination_deg': float(inclination),
        'v_sini_km_s': float(v_sini),
    }

    # Create required directories
    directories = [
        Path(path_target, target_id, "Star_Spectrum"),
        Path(path_target, target_id, ConstantVariables.CC_FOLDER),
        Path(path_target, target_id, ConstantVariables.RESULTS_FOLDER),
        Path(path_target, target_id, ConstantVariables.HR_INSTRUMENTS, "Transmission"),
        Path(path_target, target_id, ConstantVariables.HR_INSTRUMENTS, "Emission"),
        Path(path_target, target_id, ConstantVariables.LR_INSTRUMENTS, "Transmission"),
        Path(path_target, target_id, ConstantVariables.LR_INSTRUMENTS, "Emission"),
    ]

    for directory in directories:
        directory.mkdir(parents=True, exist_ok=True)

    # Write YAML file
    with open(Path(path_target, target_id, "info.yaml"), 'w') as f:
        yaml.dump(yaml_dict, f, sort_keys=False)


def insert_night_in_target(path_target: Path, target_id: str, rad_mode: str, instrument: str, night: str) -> Path:
    """Create a new night directory for a target and instrument.
    
    Args:
        path_target (Path): Base path for targets
        target_id (str): Target identifier
        rad_mode (str): Radiation mode
        instrument (str): Instrument identifier
        night (str): Night identifier

    Returns:
        Path: Path to the created night directory
    """
    path_hr = Path(path_target, target_id, "HR_Instruments", rad_mode, instrument, night)
    path_hr.mkdir(parents=True, exist_ok=True)
    return path_hr


def insert_instrument_lr_in_target(path_target: Path, target_id: str, rad_mode: str, instrument: str) -> Path:
    """Create a new instrument directory for a target.

    Args:
        path_target (Path): Base path for targets
        target_id (str): Target identifier
        rad_mode (str): Radiation mode
        instrument (str): Instrument identifier

    Returns:
        Path: Path to the created instrument directory
    """
    path_lr = Path(path_target, target_id, "LR_Instruments", rad_mode, instrument)
    path_lr.mkdir(parents=True, exist_ok=True)
    return path_lr
