import pandas as pd
import numpy as np

from .Preprocessing import classify


# Anomaly category columns (optional, keep if you still want string labels)


def detect_outliers_percentile(group, variable, date_column, eval_period):
    
    """# 📈 PERCENTILE MODEL
    ---

    The `detect_outliers_percentile` function is a robust anomaly detection tool designed to identify **statistical outliers** in
    time series or grouped data using a dynamic, **expanding window percentile approach**.

    ## 📋 Functional Overview
    The function operates by partitioning the data into an initial training set and a subsequent evaluation period. It establishes
    **"normal" behavior** based on the 5th and 95th percentiles of the available historical data, flagging any value that falls
    outside these bounds as an anomaly.

    ## 🧠 Core Logic Stages

    ### 1. Data Preparation and Validation
    > **Minimum Threshold:** The function requires at least **10 data points** to run; otherwise, it returns an empty DataFrame to
    prevent statistically insignificant results.
    >
    > **Copying:** It creates a copy of the input group to ensure the original data remains unaltered during the calculation process.

    ### 2. Initial Training Block
    * **Static Baseline:** For the first part of the data (everything before the `eval_period`), the function calculates a single
    static baseline using the 5th and 95th percentiles of the entire training block.
    * **Classification:** It applies these fixed bounds to the training rows, labeling them using a helper `classify` function and
    assigning a boolean `is_Percentile_anomaly` flag.

    ### 3. Expanding Window Evaluation
    * **Sequential Testing:** For each data point in the evaluation period (the last *n* points specified by `eval_period`), the
    function recalculates the percentiles using **all previously seen data points**.
    * **Dynamic Adaptation:** As the loop progresses, the "training set" grows. This allows the model to adapt to gradual shifts in
    the data distribution, as the thresholds for the current point are informed by every point that came before it.
    * **Real-time Simulation:** By calculating the bounds for point $i$ based only on points $0$ to $i-1$, the function simulates how
    the model would perform in a live environment.

    ## 📤 Key Output Columns
    The function appends the following columns to the returned DataFrame:
    * **`Percentile_low` / `Percentile_high`**: The specific thresholds used to evaluate that row.
    * **`Percentile_anomaly`**: A categorical label (likely "High," "Low," or "Normal") generated by the external `classify` function.
    * **`is_Percentile_anomaly`**: A boolean flag indicating whether the value was outside the 5%–95% range.

    ## 💡 Usage Context
    This function is particularly useful for detecting spikes or drops in metrics where the underlying distribution might **drift
    slowly over time**. By using percentiles rather than standard deviations, it is more resilient to extreme historical outliers
    that might otherwise skew a mean-based threshold."""
    
    n = len(group)
    if n < 10:
        # Optional: log specific keys if they exist in your scope
        return pd.DataFrame(columns=group.columns)

    group = group.copy()
    # Explicitly ensure date_column is datetime right at the start
    group[date_column] = pd.to_datetime(group[date_column])
    train_size = n - eval_period

    # --- 1. HANDLE TRAINING DATA (Initial Block) ---
    # Calculate baseline IQR using all data available before eval_period
    initial_train = group[variable].iloc[:train_size]
    
    low = initial_train.quantile(0.05)
    high= initial_train.quantile(0.95)
    
    # Assign initial bounds to the training rows
    group.loc[group.index[:train_size], 'set'] = "TRAIN"
    group.loc[group.index[:train_size], 'Percentile_low'] = low
    group.loc[group.index[:train_size], 'Percentile_high'] = high
    group.loc[group.index[:train_size], 'Percentile_anomaly'] = group[variable].iloc[:train_size].apply(
        lambda x: classify(x, low, high)
    )
    group.loc[group.index[:train_size], 'is_Percentile_anomaly'] = (
        (group[variable].iloc[:train_size] < low) | 
        (group[variable].iloc[:train_size] > high)
    )

    
    # --- 2. HANDLE EVALUATION DATA (Expanding Window) ---
    # Iterate through the eval period, increasing the training set one point at a time
    for i in range(train_size, n):
        # Data available up to this point (expanding)
        current_train = group[variable].iloc[:i]
        
        LOW = current_train.quantile(0.05)
        HIGH  = current_train.quantile(0.95)
    
        # Test the current point i
        current_val = group[variable].iloc[i]
        group.iloc[i, group.columns.get_loc('set')] = "TEST"
        group.iloc[i, group.columns.get_loc('Percentile_low')] = LOW
        group.iloc[i, group.columns.get_loc('Percentile_high')] = HIGH
        group.iloc[i, group.columns.get_loc('Percentile_anomaly')] = classify(current_val, LOW, HIGH)
        group.iloc[i, group.columns.get_loc('is_Percentile_anomaly')] = (current_val < LOW) or (current_val > HIGH)

    # Cast boolean column properly
    group['is_Percentile_anomaly'] = group['is_Percentile_anomaly'].astype(bool)
    # FINAL SAFETY CHECK
    group[date_column] = pd.to_datetime(group[date_column])
    
    return group
