from .pipeline import run_pipeline
import pandas as pd
import re
import inspect
import IPython
from .helpers.help_anomaly import help_anomaly
from .helpers.evaluation_info import evaluation_info


class Colors:
    BLUE = '\033[94m'
    RED = '\033[91m'
    END = '\033[0m'
    BOLD = '\033[1m'
    BOLD_ITALIC = "\033[1;3m"
    RESET = "\033[0m"

             
def timeseries_anomaly_detection(
    master_data = None, 
    unique_ids = None, 
    variable = None,
    date_column = None,
    freq = "W-MON",
    eval_period = 1,
    max_records = None,
    imputation_method = "median",
    percentile_threshold = 0.025,
    SD_threshold = 3,
    mad_threshold = 12, 
    mad_scale_factor = 0.6745,
    IQR_threshold = 7,
    alpha = 0.3, 
    sigma = 2, 
    prophet_CI = 0.95, 
    contamination = 0.05, 
    random_state = 42,
    enabled_models=['PERCENTILE', 'SD', 'MAD', 'IQR', 'EWMA', 'FB', 'ISF', 'DBSCAN']
):
    
    
    """
    
    
-----------------------------------------------------------------------------------------------------------
💡 DOCUMENTATION & HELP:
    Please do not use this help function. We have built custom, user-friendly help guides with this function: 
    >>> from anomaly_pipeline import help_anomaly
    >>> help_anomaly()
-----------------------------------------------------------------------------------------------------------

    """
    
    try:
        ip = IPython.get_ipython()
        if ip is None:
            raise RuntimeError("Not running in an IPython environment")
        ns = ip.user_ns
        ns['help_anomaly'] = help_anomaly
        ns['evaluation_info'] = evaluation_info
    except Exception as e:
        pass
    
    # making robust with input parameters
    if isinstance(unique_ids, str):
        unique_ids = [unique_ids]
    
    
    # --- 1. MANDATORY PARAMETER VALIDATION ---
    required_params = {
        "master_data": master_data,
        "unique_ids": unique_ids,
        "variable": variable,
        "date_column": date_column
    }

    missing_params = [name for name, val in required_params.items() if val is None]

    if missing_params:
        print("\n" + "!"*70)
        # Bold Red for the Error Header
        print(f"❌ {Colors.RED}{Colors.BOLD}ERROR: MISSING REQUIRED PARAMETERS{Colors.END}")
        print("The following parameters are required to run the detection:")
        for param in missing_params:
            print(f"  - {param}")
        
        # Blue for the Hint and Bold/Italic for the help command
        print(f"\n💡 {Colors.BLUE}HINT: Use the following for details:{Colors.END}")
        print(f"   {Colors.BOLD_ITALIC}from anomaly_pipeline import help_anomaly{Colors.END}")
        print(f"   {Colors.BOLD_ITALIC}help_anomaly(){Colors.END}")
        print(f"{Colors.BLUE}It contains description and expected formats for each parameter.{Colors.END}")
        print("!"*70 + "\n")
        return # Exit early
    
    
    # --- 2. MANDATORY COLUMN VALIDATION ---
    mandatory_cols = unique_ids + [variable, date_column]
    missing_cols = [col for col in mandatory_cols if col not in master_data.columns]
    
    if missing_cols:
        print("\n" + "!"*70)
        print(f"❌ {Colors.RED}{Colors.BOLD}ERROR: MANDATORY COLUMNS MISSING{Colors.END}")
        print(f"The following columns were not found in {Colors.BOLD_ITALIC}master_data{Colors.END}:")
        for col in missing_cols:
            print(f"  - {col}")
        
        # Blue for the Hint and Bold/Italic for the help command
        print(f"\n💡 {Colors.BLUE}HINT: Ensure columns match the spelling in your file.{Colors.END}")
        #print(f"{Colors.BLUE}Available columns: {Colors.END}{list(master_data.columns)[:5]}...")
        
        print(f"\n{Colors.BLUE}Use the following for detailed documentation:{Colors.END}")
        print(f"   {Colors.BOLD_ITALIC}from anomaly_pipeline import help_anomaly{Colors.END}")
        print(f"   {Colors.BOLD_ITALIC}help_anomaly(){Colors.END}")
        print("!"*70 + "\n")
        return # Exit early
    
    # Check if the variable is numeric
    """if not pd.api.types.is_numeric_dtype(master_data[variable]):
        raise TypeError(f"CRITICAL: The variable '{variable}' must be numeric, but found {master_data[variable].dtype}.")"""
    
    # Inside your timeseries_anomaly_detection function:
    # 1. Get the calling context (handling multi-line calls)
    frame = inspect.currentframe().f_back
    call_line = ""
    try:
        # Get several lines of context in case the call is spread out
        context = inspect.getframeinfo(frame).code_context
        if context:
            call_line = "".join([line.strip() for line in context])
    except:
        pass

    # 2. Advanced Assignment Check
    is_assigned = False
    # Look for an equals sign that isn't inside parentheses (basic check)
    if "=" in call_line and "timeseries_anomaly_detection" in call_line:
        # Split by the function name to look at what's before it
        prefix = call_line.split("timeseries_anomaly_detection")[0].strip()
        if prefix.endswith("="):
            is_assigned = True
        
    # --- 3. EXECUTE PIPELINE ---
    # Store results in a local variable first
    final_df, evaluation_report = run_pipeline(
        master_data = master_data,
        unique_ids = unique_ids,
        variable = variable,
        date_column = date_column,
        freq = freq,
        eval_period = eval_period,
        max_records = max_records, 
        imputation_method = imputation_method,
        percentile_threshold = percentile_threshold,
        SD_threshold=SD_threshold,
        IQR_threshold = IQR_threshold,
        mad_threshold = mad_threshold, 
        mad_scale_factor = mad_scale_factor,
        alpha = alpha,
        sigma  = sigma,
        prophet_CI = prophet_CI,
        contamination = contamination,
        random_state = random_state,
        is_assigned= is_assigned,
        enabled_models=enabled_models
    )


    # 3. If NOT assigned, trigger the "Auto-Save" to the global namespace
    if not is_assigned:
        from IPython import get_ipython
        shell = get_ipython()
        if shell:
            shell.user_ns['final_results'] = final_df
            shell.user_ns['evaluation_report'] = evaluation_report

    # 4. Final return logic
    if is_assigned:
        # Determine if the user assigned to a single variable or multiple
        prefix = call_line.split("=")[0].strip()
        
        # If there's no comma in the assignment prefix, they used a single variable
        if "," not in prefix:
            print(f"\n📀 INFO: You assigned the output to a single variable: '{prefix}'")
            print(f"   This variable is a tuple containing 2 DataFrames. Access them via:")
            print(f"   1. final_results     :  {prefix}[0]")
            print(f"   2. evaluation_report :  {prefix}[1]")
            print(f"   Or unpack them: final_results, evaluation_report = {prefix}\n")
        
        return final_df, evaluation_report
    else:
        # Return None so Jupyter doesn't print the "wall of text"
        return None