"""
evaluate.py  RouteRisk Temporal Holdout Evaluation
====================================================
Responsibilities:
  1. Load flight data for the holdout period (Oct 2024 - Dec 2024)
  2. Load pre-trained models, imputers, and encoders from /models
  3. Transform the holdout data using saved artifacts (inference-safe)
  4. Evaluate performance using Accuracy, Precision, Recall, F1, and ROC-AUC
"""

import sys
import os
import joblib
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

from pandas.tseries.holiday import USFederalHolidayCalendar
from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, 
    f1_score, roc_auc_score, classification_report
)

# Database connection
path_to_add = os.path.abspath('../..')
sys.path.insert(0, path_to_add)
from core.database import engine

if __name__ == '__main__':
    print("=" * 60)
    print("STEP 1 — Loading Holdout Data & Saved Artifacts")
    print("=" * 60)

    # 1. Load the temporal holdout data (October 2024 to December 2024)
    query = """
        SELECT *
        FROM flights
        WHERE origin IS NOT NULL
            AND dest IS NOT NULL
            AND carrier IS NOT NULL
            AND fl_date >= '2024-10-01'
            AND fl_date <= '2024-12-31'
    """
    df_eval = pd.read_sql(query, engine)
    print(f"  Loaded {len(df_eval):,} rows for holdout period (Oct 2024 - Dec 2024)")

    # 2. Load all saved artifacts
    best_model      = joblib.load("models/best_model.pkl")
    num_imputer     = joblib.load("models/num_imputer.pkl")
    label_encoders  = joblib.load("models/label_encoders.pkl")
    feature_columns = joblib.load("models/feature_columns.pkl")
    num_cols        = joblib.load("models/num_cols.pkl")
    
    cat_imputer_path = "models/cat_imputer.pkl"
    cat_imputer = joblib.load(cat_imputer_path) if os.path.exists(cat_imputer_path) else None

    # Load pre-computed weather rates to map to test data
    weather_rates_df = pd.read_csv("data/route_weather_rates.csv")
    weather_rate_dict = dict(zip(weather_rates_df['route_str'], weather_rates_df['route_weather_delay_rate']))

    print("  Artifacts successfully loaded.")

    print("\n" + "=" * 60)
    print("STEP 2 — Applying Inference Transformations")
    print("=" * 60)

    def apply_saved_features(df: pd.DataFrame) -> pd.DataFrame:
        """Applies feature transformations using only loaded artifacts."""
        df = df.copy()

        # Date-derived features
        df['fl_date']      = pd.to_datetime(df['fl_date'])
        df['day_of_week']  = df['fl_date'].dt.dayofweek
        df['day_of_month'] = df['fl_date'].dt.day

        # Holiday logic
        cal = USFederalHolidayCalendar()
        holidays = cal.holidays(start='2020-01-01', end='2030-12-31')
        df['days_to_holiday'] = df['fl_date'].apply(lambda d: int(min(abs((holidays - d).days))))
        df['is_holiday_week'] = (df['days_to_holiday'] <= 3).astype(int)

        # Peak travel season
        df['is_peak_season'] = df['fl_date'].dt.month.isin([6, 7, 8, 12]).astype(int)

        # Departure time buckets
        df['dep_hour']   = df['crs_dep_time'] // 100
        df['dep_period'] = pd.cut(
            df['dep_hour'],
            bins=[0, 6, 12, 17, 21, 24],
            labels=['red_eye', 'morning', 'afternoon', 'evening', 'night'],
            right=False,
        )

        # Route identifiers & pre-calculated weather mapping
        df['route'] = df['origin'] + '_' + df['dest']
        df['route_weather_delay_rate'] = df['route'].map(weather_rate_dict).fillna(0)

        # Transform categoricals using SAVED label encoders (Safe mapping for unseen data)
        for col in ['carrier', 'origin', 'dest', 'route', 'dep_period']:
            le = label_encoders[col]
            # Creates a dictionary of known classes to avoid crash on unseen test routes/carriers
            known_classes = dict(zip(le.classes_, le.transform(le.classes_)))
            df[col] = df[col].astype(str).map(known_classes).fillna(-1)

        return df

    # Transform raw holdout data
    df_processed = apply_saved_features(df_eval)
    
    # Isolate targets and align features exactly with training
    y_true = df_processed['arr_del15']
    
    # Fill missing columns with 0 if any leaked out during inference filtering
    for col in feature_columns:
        if col not in df_processed.columns:
            df_processed[col] = 0
            
    X_eval = df_processed[feature_columns]

    # Apply saved imputers
    X_eval[num_cols] = num_imputer.transform(X_eval[num_cols])
    if cat_imputer is not None:
        cat_cols = [c for c in feature_columns if c not in num_cols]
        if cat_cols:
            X_eval[cat_cols] = cat_imputer.transform(X_eval[cat_cols])

    print("  Transformations and imputations applied.")

    print("\n" + "=" * 60)
    print("STEP 3 — Temporal Holdout Evaluation")
    print("=" * 60)

    # Predict using the saved best_model
    y_pred = best_model.predict(X_eval)
    
    # Attempt to get probabilities for ROC-AUC
    if hasattr(best_model, 'predict_proba'):
        y_proba = best_model.predict_proba(X_eval)[:, 1]
    else:
        y_proba = None

    # Calculate requested metrics
    accuracy  = accuracy_score(y_true, y_pred)
    precision = precision_score(y_true, y_pred, zero_division=0)
    recall    = recall_score(y_true, y_pred, zero_division=0)
    f1        = f1_score(y_true, y_pred, zero_division=0)
    roc_auc   = roc_auc_score(y_true, y_proba) if y_proba is not None else "N/A"

    print(f"Results for Temporal Holdout (Oct 2024 - Dec 2024):\n")
    print(f"  Accuracy:  {accuracy:.4f}")
    print(f"  Precision: {precision:.4f}")
    print(f"  Recall:    {recall:.4f}")
    print(f"  F1 Score:  {f1:.4f}")
    if isinstance(roc_auc, float):
        print(f"  ROC-AUC:   {roc_auc:.4f}")
    else:
        print(f"  ROC-AUC:   {roc_auc}")
        
    print("\nDetailed Classification Report:")
    print(classification_report(y_true, y_pred, zero_division=0))