"""
train_hybrid_det.py — Training dei modelli Ridge per il modello ibrido deterministico

Addestra un modello di Ridge Regression per ogni prodotto (più uno per il delta_no_buy)
che predice la domanda media attesa in funzione dell'assortimento binario a.

Come funziona: 

  1. Carica dataset_train_deterministico.csv (generato da create_hybrid_datasets.py).
  2. Split: 85% train / 15% test.
  3. Normalizzazione output con MinMaxScaler (per stabilità numerica).
  4. Per ogni prodotto i: usa RidgeCV con 50 valori di alpha per selezionare
     il parametro di regolarizzazione ottimale via cross-validation.
  5. Riaddestra un Ridge finale con alpha ottimale.
  6. Stampa MAE e R^2 medi sul test set.

Output:
  - best_linear_models_list.pkl : lista di N+1 modelli Ridge (N prodotti + delta_no_buy)
  - scaler_y.pkl                : MinMaxScaler per l'output (da applicare ai target)
"""


import sys
import os
cartella_progetto = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if cartella_progetto not in sys.path:
    sys.path.append(cartella_progetto)
from prepare_data import *
from optimization import *
import pandas as pd
import numpy as np
import joblib
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import RidgeCV, Ridge
from sklearn.metrics import mean_absolute_error, r2_score
import matplotlib.pyplot as plt
import create_hybrid_datasets


dataset_filename_train="dataset_train_deterministico.csv"
if os.path.exists(dataset_filename_train):
    print(f"Dataset find: upload '{dataset_filename_train}'")
    df_train=pd.read_csv(dataset_filename_train)
else:
    df_train=create_hybrid_datasets.main()

num_p=int((df_train.shape[1]-1)/2)
X_quantity=df_train.iloc[:,:num_p].values
y=df_train.iloc[:,num_p:].values


X_train,X_test,y_train,y_test=train_test_split(X_quantity,y,test_size=0.15,random_state=42)

scaler_y=MinMaxScaler()
y_train_scaled=scaler_y.fit_transform(y_train)
y_test_scaled=scaler_y.transform(y_test)

alphas_to_test=np.logspace(-4,1,50)
final_models=[]
all_mae=[]
all_r2=[]

target_prods_to_graph=[1,63]
for i in range(y_train_scaled.shape[1]):
    model=RidgeCV(alphas=alphas_to_test, cv=None, store_cv_results=True)
    model.fit(X_train,y_train_scaled[:, i])
    
    test_preds=model.predict(X_test)

    y_min=scaler_y.data_min_[i]
    y_max=scaler_y.data_max_[i]
    drange=scaler_y.data_range_[i]

    test_preds_real=(test_preds*drange)+y_min
    all_mae.append(mean_absolute_error(y_test[:, i], test_preds_real))
    all_r2.append(r2_score(y_test[:, i], test_preds_real))

    if i in target_prods_to_graph:
        mean_cv_errors=model.cv_results_.mean(axis=0)
        
        plt.figure(figsize=(15, 15))
        plt.plot(alphas_to_test, mean_cv_errors, marker='o', color='purple', linewidth=2, label='Validation MSE')
        
        plt.xscale('log') 
        plt.xlabel('Valore del parametro Alpha (Scala Logaritmica)')
        plt.ylabel('Mean Squared Error (Dati Scalati)')
        plt.title(f'Cronologia Ottimizzazione Iperparametri - Prodotto {i}')
        
        plt.axvline(model.alpha_, color='red', linestyle='--', 
                    label=f'Alpha Ottimale Scelto: {model.alpha_:.4f}')
        
        plt.legend()
        plt.grid(True, which="both", linestyle="--", alpha=0.5)
        plt.show()
    best_alpha=model.alpha_ 
    final_model=Ridge(alpha=best_alpha) #per passare predittore a gurobi
    final_model.fit(X_train, y_train_scaled[:, i]) 
    final_models.append(final_model)

joblib.dump(final_models,'best_linear_models_list.pkl')
joblib.dump(scaler_y,'scaler_y.pkl') 

print(f"\n--- PERFORMANCE FINALE ---")
print(f"MAE Medio su test: {np.mean(all_mae):.4f} unità")
print(f"R2 Medio su test: {np.mean(all_r2):.4f}")


