import pandas as pd
import numpy as np
import re
from sklearn.preprocessing import PowerTransformer

# 1. Load Data
antigen1 = pd.read_excel('../xmap_biomarkers/data/02a.AP0211_GLA02_SBA01_Antigen_list.xlsx')
antigen2 = pd.read_excel('../xmap_biomarkers/data/02b. AP0211 GLA02_SBA02_Antigen_list.xlsx')
data1 = pd.read_excel('../xmap_biomarkers/data/12a. AP0211 GLA02 SBA01_Data Intensity.xlsx')
data2 = pd.read_excel('../xmap_biomarkers/data/12b. AP0211 GLA02 SBA02_Data_Intensity.xlsx')
layout = pd.read_excel('../xmap_biomarkers/data/layout.xlsx')

datasets = [data1, data2]
antigens = [antigen1, antigen2]
controls = ["Anti-human IgG", "EBNA1", "Bare-bead", "His6ABP"]

# Helper functions
def handle_background(df):
    # Empty sample is "EMPTY-0001"
    empty_df = df[df['Internal LIMS ID'] == 'EMPTY-0001']
    if empty_df.empty:
        return df
    
    # Calculate background noise
    # We only care about Analyte columns
    analyte_cols = [c for c in df.columns if c.startswith('Analyte')]
    empty_means = empty_df[analyte_cols].mean()
    median_mean = empty_means.median()
    sd_mean = empty_means.std()
    
    # subset of empty_means < median + sd
    inset = empty_means[empty_means < (median_mean + sd_mean)]
    if len(inset) < 0.95 * len(empty_means):
        calset = empty_means
    else:
        calset = inset
        
    cutoff = calset.max() + calset.std()
    
    # Keep columns where at least one sample is > cutoff
    bgmap = df[analyte_cols] > cutoff
    keep_cols = bgmap.sum() > 0
    keep_analyte_cols = [c for c in analyte_cols if keep_cols[c]]
    
    return df[['Internal LIMS ID', 'Original ID'] + keep_analyte_cols]

def emptyadjust(df):
    empty_df = df[df['Internal LIMS ID'] == 'EMPTY-0001']
    if empty_df.empty:
        return df
    
    analyte_cols = [c for c in df.columns if c.startswith('Analyte')]
    emptyvector = empty_df[analyte_cols].mean()
    
    df = df[(df['Internal LIMS ID'] != 'EMPTY-0001') & (df['Internal LIMS ID'] != 'MIX_2-0029')].copy()
    
    # Subtract empty vector
    df[analyte_cols] = df[analyte_cols].sub(emptyvector, axis=1)
    
    # set <0 to 0, then add 1
    df[analyte_cols] = df[analyte_cols].clip(lower=0) + 1
    return df

def compress_duplicates(df, layout_df):
    # Layout merges based on Tube label having a hyphen
    # Find base names before hyphen
    tube_labels = layout_df['Tube label'].dropna()
    hyphen_labels = tube_labels[tube_labels.str.contains('-')]
    
    base_names = []
    for lbl in hyphen_labels:
        base = lbl.split('-')[0]
        if base not in base_names:
            base_names.append(base)
            
    analyte_cols = [c for c in df.columns if c.startswith('Analyte')]
            
    for base in base_names:
        # Find sample ids in layout matching base$ or base-
        pattern = f"^{base}$|^{base}-"
        matching_layout = layout_df[layout_df['Tube label'].str.contains(pattern, regex=True, na=False)]
        mergerows = matching_layout['Sample id_LIMS'].dropna().tolist()
        
        if not mergerows:
            continue
            
        # Find these sample ids in df
        regex_pattern = '|'.join(mergerows)
        matching_idx = df['Internal LIMS ID'].str.contains(regex_pattern, regex=True, na=False)
        
        if matching_idx.sum() > 0:
            # calculate mean
            mean_vals = df.loc[matching_idx, analyte_cols].mean()
            # replace first occurrence
            first_idx = df[matching_idx].index[0]
            df.loc[first_idx, analyte_cols] = mean_vals
            # drop others
            drop_idx = df[matching_idx].index[1:]
            df = df.drop(drop_idx)
            
    return df

def set_colname_adapter(df, antigen_df):
    mapping = {}
    for col in df.columns:
        if col.startswith('Analyte'):
            num = int(col.split(' ')[1])
            match = antigen_df[antigen_df['BeadID (Analyte)'] == num]
            if not match.empty:
                antigen_name = match.iloc[0]['Antigen name']
                mapping[col] = antigen_name
    df = df.rename(columns=mapping)
    # Remove controls
    drop_cols = [c for c in df.columns if c in controls]
    df = df.drop(columns=drop_cols)
    return df

# Apply stage 1
processed_datasets = []
for i in range(2):
    df = datasets[i]
    df = handle_background(df)
    df = emptyadjust(df)
    df = compress_duplicates(df, layout)
    df = set_colname_adapter(df, antigens[i])
    # Extract Group based on Original ID (GC -> 1, HD -> 0, else NaN)
    df['group'] = df['Original ID'].apply(lambda x: 1 if 'GC' in str(x) else (0 if 'HD' in str(x) else np.nan))
    processed_datasets.append(df)

# Stage 2: Merge down
df1, df2 = processed_datasets
# Align columns: Internal LIMS ID, Original ID, group
common_keys = ['Internal LIMS ID', 'Original ID', 'group']
all_cols = set(df1.columns).union(set(df2.columns))
analyte_cols_all = list(all_cols - set(common_keys))

# Since df1 and df2 have same rows, we can merge on Internal LIMS ID
merged = pd.merge(df1, df2, on=['Internal LIMS ID', 'Original ID', 'group'], how='outer', suffixes=('_1', '_2'))

# Average common columns
final_cols = {}
for col in analyte_cols_all:
    if col + '_1' in merged.columns and col + '_2' in merged.columns:
        merged[col] = merged[[col + '_1', col + '_2']].mean(axis=1)
        merged = merged.drop(columns=[col + '_1', col + '_2'])
    elif col + '_1' in merged.columns:
        merged = merged.rename(columns={col + '_1': col})
    elif col + '_2' in merged.columns:
        merged = merged.rename(columns={col + '_2': col})

# Drop rows with NaN group
merged = merged.dropna(subset=['group'])
merged = merged.reset_index(drop=True)

# Separate features and target
X = merged.drop(columns=common_keys)
y = merged['group']
meta = merged[['Internal LIMS ID', 'Original ID']]

# Box-Cox transformation + Standardization via PowerTransformer
pt = PowerTransformer(method='box-cox', standardize=True)
# Ensure strictly positive values for Box-Cox
min_val = X.min().min()
if min_val <= 0:
    X = X - min_val + 1e-5

X_transformed = pt.fit_transform(X)
X_transformed_df = pd.DataFrame(X_transformed, columns=X.columns)

# Final dataset
final_df = pd.concat([meta, y.astype(int), X_transformed_df], axis=1)

# Now, create the slices!
# 1) Significant 7
sig_7 = ['HPRA000767', 'HPRA034083', 'HPRA006876', 'HPRA019035', 'HPRA003490', 'HPRA022019', 'HPRA017192']
# Filter out any that might have been dropped during background removal
sig_7_present = [c for c in sig_7 if c in final_df.columns]
slice_1 = final_df[common_keys + sig_7_present]
slice_1.to_csv('slice_1_significant_7.csv', index=False)

# 2) Significant 7 + 33 random proteins = 40 total
np.random.seed(42)
other_proteins = [c for c in X.columns if c not in sig_7_present]
random_33 = np.random.choice(other_proteins, size=min(33, len(other_proteins)), replace=False).tolist()
slice_2_cols = sig_7_present + random_33
slice_2 = final_df[common_keys + slice_2_cols]
slice_2.to_csv('slice_2_sig7_plus_33_random.csv', index=False)

# 3) All proteins
final_df.to_csv('slice_3_all_proteins.csv', index=False)

print("Preprocessing complete. Slices saved.")
