import pandas as pd
import numpy as np

from typing import Union

def bootstrap_simulate(
                    data: Union[np.array, pd.DataFrame] = None, 
                    n_samples: int = 1500, 
                    n_steps: int = None):
    """
    Simulates bootstrap samples using a random walk process based on the input data.
    
    The function follows the principles of bootstrapping, which is a resampling method used to estimate the distribution of a statistic from a sample of data. Bootstrapping involves repeatedly resampling the original data with replacement to create new samples of the same size as the original sample. Each new sample is used to calculate the statistic of interest, and the distribution of these calculated statistics is used to estimate the distribution of the statistic in the population.

    Args:
        data: A 1D array-like object containing the original data.
        n_samples: An integer representing the number of bootstrap samples to generate.
        n_steps: An integer representing the number of steps in the random walk process.

    Returns:
        An array of shape (n_samples, n_steps) containing the bootstrap samples generated by the random walk process.
    """
    # Initialize array to hold bootstrap samples
    bootstrap_samples = np.empty((n_samples, n_steps))
    
    # Loop over number of bootstrap samples
    for i in range(n_samples):
        # Resample data with replacement
        bootstrap_data = np.random.choice(data, size=len(data))#, replace=True)
        
        # Simulate random walk based on bootstrap data
        walk = np.cumsum(np.random.randn(n_steps))
        walk -= walk[0]
        walk *= bootstrap_data.std() / walk.std()
        walk += bootstrap_data.mean()
        
        # Save random walk as one of the bootstrap samples
        bootstrap_samples[i] = walk
    
    return bootstrap_samples
    
def bootstrap_p_value(
                    control: Union[np.array, pd.DataFrame] = None, 
                    treatment: Union[np.array, pd.DataFrame] = None, 
                    simulations: np.array = None, 
                    alpha: float = 0.05):
    """
    Calculate the p-value and false positive rate for a two-sample hypothesis test using bootstrapping
    with a specified number of simulations.

    Args:
        control: A 1-D array-like object of control group data.
        treatment: A 1-D array-like object of treatment group data.
        simulations: A 2-D array-like object containing bootstrap simulations of the data.
        center: A boolean indicating whether to center the data around the control mean (default False).
        alpha: The significance level for the hypothesis test (default 0.05).

    Returns:
        A tuple containing the p-value and false positive rate, the lower and upper bounds of the confidence
        interval, and the bootstrapped means of the simulations.

    Raises:
        ValueError: If either control or treatment is empty, or if simulations is empty.

    Examples:
        >>> control = [1, 2, 3, 4, 5]
        >>> treatment = [3, 4, 5, 6, 7]
        >>> bs_sims = [np.random.choice(control, size=len(control), replace=True) for i in range(1000)]
        >>> simulations_bootstrap_p_value(control, treatment, bs_sims, center=True, alpha=0.01)
        ([0.018, 0.0], [-3.125, 2.875], array([-1.8, -1.2, -1.2, ...,  1.8,  1.8,  1.8]))
    """
        
    # Calculate the mean of the data
    mean = np.mean(control)
    mean_treatment = np.mean(treatment)
        
    bootstrapped_means = np.empty(len(simulations))
    
    for i in range(len(simulations)):
        bootstrapped_means[i] = simulations[i].mean()
    
    lower, upper = np.percentile(bootstrapped_means, [alpha / 2 * 100, (1 - alpha / 2) * 100])
    
    fp_rate = len(bootstrapped_means[(bootstrapped_means >= (- mean_treatment)) & 
                                            (bootstrapped_means <= mean_treatment)]) / len(simulations)
    
    p_mean_negative = len(bootstrapped_means[bootstrapped_means < (- mean_treatment)]) / len(simulations)
    p_mean_positive = len(bootstrapped_means[bootstrapped_means > mean_treatment]) / len(simulations)
    
    p_value = p_mean_positive + p_mean_negative
    return [p_value, fp_rate], [lower, upper], bootstrapped_means