"""
Background sum cross-correlation computation module.

This script is launched as a subprocess by Frame_sum_CC.py to perform
the Kp/Vsys analysis without blocking the GUI. It reads parameters from
exchange_sum_cc.pkl, runs the computation, and writes results to
result_sum_cc.pkl or errors to error_sum_cc.pkl.

Usage:
    python3 sum_cc_background.py <exchange_path> <path_default>
"""

import sys
import os
import traceback
import pickle
from pathlib import Path

import matplotlib

from GUIBRUSHR.General_Constants.FunctionsAndConstants.radvel_g import rv_drive, timetrans_to_timeperi

matplotlib.rcParams['agg.path.chunksize'] = 10000
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import gridspec
from numpy import (array, append, arange, sin, pi, where, mean, argsort, std,
                   reshape, float64, sort, isnan, max as maxnp, min as minnp)
from scipy import stats, ndimage
from scipy.interpolate import interp1d


# ---------------------------------------------------------------------------
# Static helper functions (originally @staticmethod methods)
# ---------------------------------------------------------------------------

def _setup_output_paths(mode, path_result, path_instrument, dates,
                        tell_rm_method, order_sel, model_name):
    """Setup output directory and file prefix based on processing mode."""
    if mode == 0:
        base_folder = Path(path_result, "cross_correlation")
        prefix = ""
    else:
        if len(dates) > 1:
            str_nights = "_".join(dates)
            prefix = "multi_" + str_nights
        else:
            prefix = ""
        base_folder = Path(path_instrument[0], dates[0], "cross_correlation",
                           tell_rm_method, order_sel, model_name)
    return base_folder, prefix


def _create_output_file_paths(base_folder, prefix):
    """Create dictionary of all output file paths."""
    output_paths = Path(base_folder, prefix)
    return {
        'fits_profile': str(Path(output_paths, "ppp.fits")),
        'fits_avg_profile': str(Path(output_paths, "ttt.fits")),
        'pkl_phase_vel': str(Path(output_paths, "fase_vbar.pkl")),
        'pkl_phase': str(Path(output_paths, "phtb.pkl")),
        'pkl_multi_results': str(Path(output_paths, "results.pkl")),
        'pkl_fine_results': str(Path(output_paths, "fine.pkl")),
        'pdf_hist': str(Path(output_paths, "hist.pdf")),
        'png_hist': str(Path(output_paths, "hist.png")),
        'pdf_cc': str(Path(output_paths, "cc_norm.pdf")),
        'png_cc': str(Path(output_paths, "cc_norm.png")),
        'pkl_sum_cc': str(Path(output_paths, "sum_cc.pkl")),
        'ppp_multi_binned': str(Path(output_paths, "ppp_multi_binned.fits")),
    }


def _load_cc_data(run_cc_folder):
    """Load cross-correlation data and metadata for a single observation."""
    from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import (
        read_fits
    )
    cc_file = str(Path(run_cc_folder, "sss.fits"))
    range_pkl = str(Path(run_cc_folder, "range.pkl"))

    with open(range_pkl, "rb") as f:
        range_data = pickle.load(f)
        min_rv = range_data["min_rv"]
        max_rv = range_data["max_rv"]
        step_rv = range_data["step_rv"]
        phase_obs = range_data["phase_obs"]
        barycentric_velocity = range_data["barycentric_velocity"]
        hjd_ck = range_data["hjd_ck"]
        limit_phase = range_data["limit_phase"]

    rv_array = arange(int(min_rv), int(max_rv) + 1, int(step_rv))
    cc_matrix = read_fits(cc_file, transpose=True)

    return cc_matrix, rv_array, phase_obs, barycentric_velocity, hjd_ck, limit_phase


def _sort_data_by_phase(combined_cc_matrix, all_phases, all_bary_velocities, all_is_in_phase):
    """Sort the combined cross-correlation data by orbital phase."""
    cc_matrix_sorted = array(combined_cc_matrix)
    phases = array(all_phases)

    phases[where(phases > 0.5)] -= 1

    sort_idx = argsort(phases)
    cc_matrix_sorted = cc_matrix_sorted[:, sort_idx]
    phases = phases[sort_idx]
    bary_velocities_sorted = all_bary_velocities[sort_idx]
    all_is_in_phase = all_is_in_phase[sort_idx]

    return cc_matrix_sorted, phases, bary_velocities_sorted, all_is_in_phase


def _check_phase_condition(phase, rad_mode, limit_phase):
    """Check if the current phase meets the criteria for the given radiative mode."""
    if rad_mode == "Transmission":
        return abs(phase) < limit_phase or abs(phase) > 1 - limit_phase
    else:
        return True


def _calculate_velocity_shift_bg(phase, kp_value, all_hjd_ck, index,
                                  eccentricity_flag, target_information):
    """Calculate the velocity shift for a given phase and Kp value."""
    if eccentricity_flag:
        if -999 in all_hjd_ck:
            T_periastro = timetrans_to_timeperi(
                0, 1, target_information.eccentricity,
                target_information.argument_of_periastron - pi
            )
            orbital_element = array([
                1, T_periastro, target_information.eccentricity,
                target_information.argument_of_periastron, kp_value
            ])
            velocity_shift = rv_drive(
                array([phase]), orbital_element
            )[0]
        else:
            T_periastro = timetrans_to_timeperi(
                target_information.hjd0, target_information.period,
                target_information.eccentricity,
                target_information.argument_of_periastron - pi
            )
            orbital_element = array([
                target_information.period, T_periastro,
                target_information.eccentricity,
                target_information.argument_of_periastron, kp_value
            ])
            velocity_shift = rv_drive(
                all_hjd_ck, orbital_element
            )[index]
    else:
        velocity_shift = kp_value * sin(2 * pi * phase)

    return velocity_shift


def _analyze_kp_values_bg(kp_values, phases, bary_velocities_sorted,
                           cc_matrix_sorted, rv_array, all_hjd_ck,
                           mfilter, output_paths, all_is_in_phase,
                           eccentricity_flag, target_information):
    """
    Analyze different Kp values to find the optimal planetary radial velocity.
    Raises ValueError if the interpolation is out of the CCF map range.
    """
    from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import (create_fits
    )
    num_kp = len(kp_values)
    best_kp = 0
    best_v_rest = 0
    max_corr_value = 0
    averaged_profiles = []
    reference_profile = None

    for i in range(num_kp):
        shifted_profiles = []

        for j in range(len(phases)):
            if all_is_in_phase[j]:
                velocity_shift = _calculate_velocity_shift_bg(
                    phases[j], kp_values[i], all_hjd_ck, j,
                    eccentricity_flag, target_information
                )
                total_shift = (velocity_shift +
                               target_information.systemic_velocity -
                               bary_velocities_sorted[j])

                if rv_array.min() < total_shift < rv_array.max():
                    interp_func = interp1d(rv_array, cc_matrix_sorted[:, j],
                                          kind="linear", bounds_error=False, fill_value=0)
                    shifted_profile = interp_func(rv_array + total_shift)
                else:
                    raise ValueError(
                        f"Requested RV is outside the CCF map range "
                        f"[{rv_array.min():.1f}, {rv_array.max():.1f}] km/s.\n\n"
                        "Please recompute the CCF maps with a wider RV range."
                    )
                shifted_profiles.append(shifted_profile)

        if mfilter:
            shifted_profiles = [
                profile - ndimage.uniform_filter1d(profile, size=40)
                for profile in shifted_profiles
            ]

        shifted_profiles = array(shifted_profiles)
        avg_profile = mean(shifted_profiles, axis=0)

        rv_indices = where((rv_array > -55) & (rv_array < 55))[0]
        avg_profile_subset = avg_profile[rv_indices]
        rv_subset = rv_array[rv_indices]

        if maxnp(avg_profile_subset) > max_corr_value and kp_values[i] > 0:
            best_kp = kp_values[i]
            best_v_rest = rv_subset[where(avg_profile_subset == maxnp(avg_profile_subset))[0]][0]
            max_corr_value = maxnp(avg_profile_subset)
            create_fits(shifted_profiles, output_paths['fits_profile'])
            reference_profile = shifted_profiles

        if i == 0:
            reference_profile = shifted_profiles

        averaged_profiles.append(avg_profile)

    averaged_profiles = array(averaged_profiles)

    return best_kp, best_v_rest, max_corr_value, averaged_profiles, reference_profile


def _save_optimal_profile_data(kp_values, best_kp, averaged_profiles,
                                rv_array, output_paths):
    """Save the optimal profile data and create restricted RV range file."""
    from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import create_fits
    best_kp_index = where(kp_values == best_kp)[0][0]
    optimal_avg_profile = averaged_profiles[best_kp_index, :]

    with open(output_paths['pkl_phase_vel'], "wb") as f:
        pickle.dump(where(kp_values == best_kp)[0], f)
        pickle.dump(optimal_avg_profile, f)

    rv_analysis_indices = where((rv_array > -70) & (rv_array < 70))[0]
    create_fits(averaged_profiles[:, rv_analysis_indices], output_paths['fits_avg_profile'])


def _create_histogram_plots(in_trail_vals, out_of_trail_vals, output_paths):
    """Create and save histogram plots for noise analysis."""
    bin_width = std(out_of_trail_vals) / 4

    _, bins_out, _ = plt.hist(
        out_of_trail_vals, alpha=0.5, color="#ff8c00", density=True,
        label="Out-of-trail",
        bins=arange(minnp(out_of_trail_vals),
                    maxnp(out_of_trail_vals) + bin_width, bin_width)
    )
    mu_out, sigma_out = stats.norm.fit(out_of_trail_vals)
    best_fit_line_out = stats.norm.pdf(bins_out, mu_out, sigma_out)
    plt.plot(bins_out, best_fit_line_out, color="#ff8c00")

    _, bins_in, _ = plt.hist(
        in_trail_vals, alpha=0.5, color="#8b0000", density=True,
        label="In-trail",
        bins=arange(minnp(out_of_trail_vals),
                    maxnp(out_of_trail_vals) + bin_width, bin_width)
    )
    mu_in, sigma_in = stats.norm.fit(in_trail_vals)
    best_fit_line_in = stats.norm.pdf(bins_in, mu_in, sigma_in)
    plt.plot(bins_in, best_fit_line_in, color="#8b0000")

    plt.legend()
    plt.savefig(fname=output_paths['pdf_hist'], bbox_inches="tight", pad_inches=0.1)
    plt.savefig(fname=output_paths['png_hist'], bbox_inches="tight", pad_inches=0.1)
    plt.clf()


def _perform_noise_analysis_and_visualization_bg(reference_profile, rv_array,
                                                  best_v_rest, output_paths,
                                                  min_rv_std, max_rv_std):
    """Perform noise analysis and create histogram visualization."""
    oot_indices = where(
        ((rv_array >= best_v_rest - max_rv_std) & (rv_array <= best_v_rest - min_rv_std)) |
        ((rv_array >= best_v_rest + min_rv_std) & (rv_array <= best_v_rest + max_rv_std))
    )[0]
    in_trail_indices = where(
        (rv_array >= best_v_rest - 3) & (rv_array <= best_v_rest + 3)
    )[0]

    in_trail_vals = reshape(reference_profile[:, in_trail_indices], -1)
    out_of_trail_vals = reshape(reference_profile[:, oot_indices], -1)

    _create_histogram_plots(in_trail_vals, out_of_trail_vals, output_paths)


def _create_phase_binned_profiles_bg(phases, bary_velocities_sorted,
                                      cc_matrix_sorted, rv_array, best_kp,
                                      output_paths, target_information):
    """
    Create phase-binned profiles using the best Kp value.
    Raises ValueError if the interpolation is out of the CCF map range.
    """
    from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import (
        create_fits
    )
    recomputed_profiles = []
    for j in range(len(phases)):
        total_shift = (best_kp * sin(2 * pi * phases[j]) +
                       target_information.systemic_velocity -
                       bary_velocities_sorted[j])
        if rv_array.min() < total_shift < rv_array.max():
            interp_func = interp1d(rv_array, reshape(cc_matrix_sorted[:, j], -1),
                                   bounds_error=False, fill_value=0)
            shifted_profile = interp_func(rv_array + total_shift)
        else:
            raise ValueError(
                f"Requested RV is outside the CCF map range "
                f"[{rv_array.min():.1f}, {rv_array.max():.1f}] km/s.\n\n"
                "Please recompute the CCF maps with a wider RV range."
            )
        recomputed_profiles.append(shifted_profile)

    recomputed_profiles = array(recomputed_profiles)

    binned_profiles = []
    binned_phases = []
    phase_bin = minnp(phases)

    while phase_bin <= maxnp(phases):
        phase_idx = where((phases >= phase_bin) & (phases < phase_bin + 0.003))[0]
        if len(phase_idx) >= 1:
            binned_profiles.append(mean(recomputed_profiles[phase_idx, :], axis=0))
            binned_phases.append(phase_bin)
        phase_bin += 0.0015

    binned_profiles = array(binned_profiles)
    binned_phases = array(binned_phases)

    create_fits(binned_profiles, output_paths['ppp_multi_binned'])

    with open(output_paths['pkl_phase'], "wb") as f:
        pickle.dump(binned_phases, f)


def _estimate_noise_std(optimal_avg_profile, rv_array,
                        rv_analysis_indices, best_v_rest, min_rv_std, max_rv_std):
    """Estimate the noise standard deviation from out-of-trail regions."""
    left_region = optimal_avg_profile[where(
        (rv_array[rv_analysis_indices] >= best_v_rest - max_rv_std) &
        (rv_array[rv_analysis_indices] <= best_v_rest - min_rv_std)
    )]
    left_region = sort(left_region)[1: len(left_region) - 2]
    noise_std_left = std(left_region, dtype=float64)

    right_region = optimal_avg_profile[where(
        (rv_array[rv_analysis_indices] >= best_v_rest + min_rv_std) &
        (rv_array[rv_analysis_indices] <= best_v_rest + max_rv_std)
    )]
    right_region = sort(right_region)[1: len(right_region) - 2]
    noise_std_right = std(right_region, dtype=float64)

    if isnan(noise_std_left):
        noise_std = noise_std_right
    elif isnan(noise_std_right):
        noise_std = noise_std_left
    else:
        noise_std = (noise_std_left + noise_std_right) / 2.0

    return noise_std


def _calculate_kp_errors(kp_values, snr_profile, signal_to_noise, best_kp):
    """Calculate upper and lower error bounds for Kp."""
    upper_candidates = kp_values[where(
        (snr_profile <= signal_to_noise - 1) & (kp_values > best_kp)
    )]
    error_upper = minnp(upper_candidates) - best_kp if len(upper_candidates) > 0 else maxnp(kp_values) - best_kp

    lower_candidates = kp_values[where(
        (snr_profile <= signal_to_noise - 1) & (kp_values < best_kp)
    )]
    if best_kp == minnp(kp_values) or len(lower_candidates) == 0:
        print("Inferior error impossible to calculate, best_kp = minimum")
        error_lower = minnp(kp_values)
    else:
        error_lower = best_kp - maxnp(lower_candidates)

    return error_upper, error_lower


def _create_main_visualization_bg(averaged_profiles, rv_array, rv_analysis_indices,
                                   noise_std, optimal_avg_profile, best_v_rest,
                                   kp_values, snr_profile, best_kp,
                                   error_upper, error_lower, signal_to_noise,
                                   output_paths, reference_kp, selected_cmap_name):
    """Create the main cross-correlation visualization with multiple panels."""
    from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables
    selected_cmap = ConstantVariables.CMAPS.get(selected_cmap_name, "inferno")

    sum_cc_dictionary = {
        "final_sum_cc": averaged_profiles[:, rv_analysis_indices] / noise_std,
        "averaged_profiles": averaged_profiles,
        "rv_analysis_indices": rv_analysis_indices,
        "noise_std": noise_std,
        "rv_array": rv_array,
        "best_v_rest": best_v_rest,
        "optimal_avg_profile": optimal_avg_profile,
        "snr_profile": snr_profile,
        "best_kp": best_kp,
        "kp_values": kp_values,
        "error_upper": error_upper,
        "error_lower": error_lower,
        "signal_to_noise": signal_to_noise,
        "reference_kp": reference_kp,
        "selected_cmap_name": selected_cmap_name,
    }
    with open(output_paths['pkl_sum_cc'], "wb") as f:
        pickle.dump(sum_cc_dictionary, f)

    fig = plt.figure(figsize=(12, 10))
    fig.suptitle(f"SNR = {signal_to_noise:.2f}", fontsize=16)
    fig.subplots_adjust(right=0.8)

    gs = gridspec.GridSpec(4, 4, wspace=0.0, hspace=0.0)

    ax_main = fig.add_subplot(gs[1:4, 0:3])
    im = ax_main.imshow(
        averaged_profiles[:, rv_analysis_indices] / noise_std,
        origin="lower", aspect="auto",
        extent=(rv_array[rv_analysis_indices][0],
                rv_array[rv_analysis_indices][-1],
                kp_values[0], kp_values[-1]),
        cmap=selected_cmap
    )
    ax_main.scatter(best_v_rest, best_kp, marker="+", color="blue", label="Best", s=90)
    ax_main.set_xlabel(r"$V_{Rest}$ [km/s]")
    ax_main.set_ylabel(r"$K_p$ [km/s]")
    ax_main.axvline(0, linestyle='--', color='white', label=r"$V_{Rest}$ = 0")
    ax_main.axhline(reference_kp, linestyle='--', color='white', label=r"Reference $K_p$")

    ax_top = fig.add_subplot(gs[0, 0:3], sharex=ax_main)
    ax_top.plot(rv_array[rv_analysis_indices], optimal_avg_profile / noise_std,
                label="Optimal avg profile")
    ax_top.axhline(3, linestyle=":", color="r", label="3σ level")
    ax_top.axvline(best_v_rest, color="r",
                   label=r"Best $V_{Rest} = $" + str(best_v_rest), alpha=0.7)
    ax_top.axvline(0, linestyle="--", color="g", label=r"$V_{Rest} = 0$", alpha=0.7)
    ax_top.set_ylabel("SNR")
    ax_top.legend(loc="upper right")
    plt.setp(ax_top.get_xticklabels(), visible=False)

    ax_right = fig.add_subplot(gs[1:4, 3], sharey=ax_main)
    ax_right.plot(snr_profile, kp_values, label="SNR profile")
    ax_right.yaxis.set_label_position("right")
    ax_right.yaxis.tick_right()
    ax_right.set_ylabel(r"$K_p$ [km/s]")
    ax_right.set_xlabel("SNR")
    ax_right.set_xlim(0, signal_to_noise + 1)

    label_kp = (r"Fitted $K_p$ " +
                f"$= {{{best_kp}}}^{{+{error_upper}}}_{{-{error_lower}}}$")
    ax_right.axhline(best_kp, label=label_kp, color="r", alpha=0.7)
    ax_right.axhline(reference_kp, linestyle="--",
                     color="g", label=r"Reference $K_p$", alpha=0.7)

    mask = ((kp_values > best_kp - error_lower) &
            (kp_values < best_kp + error_upper))
    ax_right.fill_betweenx(kp_values[mask], 0, snr_profile[mask],
                           alpha=0.5, label="1-sigma interval")
    ax_right.legend(loc="upper right")

    cbar_ax = fig.add_axes([0.83, 0.15, 0.02, 0.7])
    cbar = fig.colorbar(im, cax=cbar_ax)
    cbar.set_label("SNR (dimensionless)")

    plt.savefig(fname=output_paths['pdf_cc'], bbox_inches="tight", pad_inches=0.1)
    plt.savefig(fname=output_paths['png_cc'], bbox_inches="tight", pad_inches=0.1)
    plt.close(fig)
    plt.clf()


def _save_final_results(best_kp, best_v_rest, signal_to_noise,
                        error_upper, error_lower, noise_std, rv_array,
                        kp_values, output_paths):
    """Save the final analysis results to pickle files."""
    with open(output_paths['pkl_multi_results'], "wb") as f:
        pickle.dump(best_kp, f)
        pickle.dump(best_v_rest, f)
        pickle.dump(signal_to_noise, f)
        pickle.dump(error_upper, f)
        pickle.dump(error_lower, f)

    with open(output_paths['pkl_fine_results'], "wb") as f:
        pickle.dump(noise_std, f)
        pickle.dump(rv_array, f)
        pickle.dump(kp_values, f)
        pickle.dump(error_upper, f)
        pickle.dump(error_lower, f)


def _calculate_final_results_and_visualize_bg(averaged_profiles, rv_array,
                                               best_v_rest, kp_values, best_kp,
                                               max_corr_value, output_paths,
                                               rv_analysis_limit, min_rv_std, max_rv_std,
                                               target_information, selected_cmap_name):
    """Calculate final results and create the main visualization."""
    rv_analysis_indices = where(
        (rv_array > -rv_analysis_limit) & (rv_array < rv_analysis_limit)
    )[0]
    optimal_avg_profile = averaged_profiles[
        where(kp_values == best_kp)[0][0], rv_analysis_indices
    ]

    noise_std = _estimate_noise_std(optimal_avg_profile, rv_array,
                                    rv_analysis_indices, best_v_rest,
                                    min_rv_std, max_rv_std)

    signal_to_noise = max_corr_value / noise_std
    snr_profile_matrix = averaged_profiles[:, rv_array == best_v_rest] / noise_std
    snr_profile = snr_profile_matrix[:, 0]

    error_upper, error_lower = _calculate_kp_errors(
        kp_values, snr_profile, signal_to_noise, best_kp
    )

    _create_main_visualization_bg(
        averaged_profiles, rv_array, rv_analysis_indices, noise_std,
        optimal_avg_profile, best_v_rest, kp_values, snr_profile,
        best_kp, error_upper, error_lower, signal_to_noise, output_paths,
        reference_kp=target_information.kp,
        selected_cmap_name=selected_cmap_name
    )

    _save_final_results(best_kp, best_v_rest, signal_to_noise,
                        error_upper, error_lower, noise_std, rv_array,
                        kp_values, output_paths)

    print(f"Fitted Kp = {best_kp} +{error_upper} -{error_lower} km/s")


# ---------------------------------------------------------------------------
# Main background function
# ---------------------------------------------------------------------------

def sum_cc_func_bg(params):
    """
    Background version of sum_cc_func.

    Reads all parameters from the exchange dictionary and performs the
    Kp/Vsys analysis. Returns a dict with the output png paths for GUI update.
    """
    from GUIBRUSHR.GUI.DataInterface.DataInterface import get_target_info
    path_result = params["path_result"]
    path_instrument = params["path_instrument"]
    order_sel = params["order_sel"]
    dates = params["dates"]
    instruments = params["instruments"]
    target = params["target"]
    mfilter = params["mfilter"]
    tell_rm_method = params["tell_rm_method"]
    rad_mode = params["rad_mode"]
    model_name = params["model_name"]
    mode = params["mode"]
    path_folder_targets = params["path_folder_targets"]

    # GUI widget values
    min_kp = params["min_kp"]
    max_kp = params["max_kp"]
    step_kp = params["step_kp"]
    eccentricity_flag = params["eccentricity"]
    min_rv_std = params["min_rv_std"]
    max_rv_std = params["max_rv_std"]
    rv_analysis_limit = params["rv_analysis_limit"]
    selected_cmap_name = params["selected_cmap_name"]

    # Set up output paths
    base_folder, prefix = _setup_output_paths(
        mode, path_result, path_instrument, dates, tell_rm_method,
        order_sel, model_name
    )
    output_paths = _create_output_file_paths(base_folder, prefix)

    print(f"\n\n{'-'*73}\nSaving in:", base_folder)

    # Initialize accumulators
    num_dates = len(dates)
    all_bary_velocities = array([])
    all_phases = array([])
    all_hjd_ck = array([])
    all_is_in_phase = array([])
    combined_cc_matrix = None
    rv_array = None

    print(f"\nNights: {num_dates}")

    for i in range(num_dates):
        print(f"\nInstrument: {instruments[i]} Night: {dates[i]}")

        if mode == 0:
            run_cc_folder = Path(path_result, "cross_correlation",
                                 instruments[i], dates[i])
        else:
            run_cc_folder = Path(path_instrument[i], dates[i],
                                 "cross_correlation", tell_rm_method,
                                 order_sel, model_name)

        cc_matrix, rv_array, phase_obs, barycentric_velocity, hjd_ck, limit_phase = (
            _load_cc_data(run_cc_folder)
        )

        all_phases = append(all_phases, phase_obs)
        all_bary_velocities = append(all_bary_velocities, barycentric_velocity)
        all_hjd_ck = append(all_hjd_ck, hjd_ck)
        for ph in phase_obs:
            phase_condition = _check_phase_condition(ph, rad_mode, limit_phase)
            all_is_in_phase = append(all_is_in_phase, phase_condition)

        if i == 0:
            combined_cc_matrix = cc_matrix
        else:
            combined_cc_matrix = append(combined_cc_matrix, cc_matrix, axis=1)

    cc_matrix_sorted, phases, bary_velocities_sorted, all_is_in_phase_sorted = (
        _sort_data_by_phase(combined_cc_matrix, all_phases, all_bary_velocities, all_is_in_phase)
    )

    target_information = get_target_info(path_folder_targets, target, rad_mode)
    kp_values = arange(min_kp, max_kp + 1, step_kp)

    (best_kp, best_v_rest, max_corr_value, averaged_profiles,
     reference_profile) = _analyze_kp_values_bg(
        kp_values, phases, bary_velocities_sorted, cc_matrix_sorted,
        rv_array, all_hjd_ck, mfilter, output_paths, all_is_in_phase_sorted,
        eccentricity_flag, target_information
    )

    _save_optimal_profile_data(kp_values, best_kp, averaged_profiles, rv_array, output_paths)

    print(f"best_kp: {best_kp}, best_v_rest: {best_v_rest}")

    _perform_noise_analysis_and_visualization_bg(
        reference_profile, rv_array, best_v_rest, output_paths, min_rv_std, max_rv_std
    )

    _create_phase_binned_profiles_bg(
        phases, bary_velocities_sorted, cc_matrix_sorted, rv_array,
        best_kp, output_paths, target_information
    )

    _calculate_final_results_and_visualize_bg(
        averaged_profiles, rv_array, best_v_rest, kp_values, best_kp,
        max_corr_value, output_paths, rv_analysis_limit, min_rv_std, max_rv_std,
        target_information, selected_cmap_name
    )

    return {
        "png_hist": output_paths['png_hist'],
        "png_cc": output_paths['png_cc'],
    }


def main():
    exchange_path = sys.argv[1]
    path_default = sys.argv[2]
    sys.path.append(path_default)
    os.chdir(path_default)

    with open(str(Path(exchange_path, "exchange_sum_cc.pkl")), "rb") as f:
        params = pickle.load(f)

    try:
        result = sum_cc_func_bg(params)
        with open(str(Path(exchange_path, "result_sum_cc.pkl")), "wb") as f:
            pickle.dump(result, f)
    except Exception as e:
        with open(str(Path(exchange_path, "error_sum_cc.pkl")), "wb") as f:
            pickle.dump(str(e) + "\n" + traceback.format_exc(), f)


if __name__ == "__main__":
    main()
