"""
CMI2025 Simple RandomForest Submission
エラーが出ないことを最優先にした安全な提出用推論サーバー
"""

import sys
sys.path.append('/kaggle/input/cmi-detect-behavior-with-sensor-data/kaggle_evaluation')

import polars as pl
import numpy as np
import warnings
from typing import Optional, List, Any

warnings.filterwarnings('ignore')

# Kaggle環境でのインポート
try:
    from kaggle_evaluation.core.templates import InferenceServer
    from kaggle_evaluation.cmi_gateway import CMIGateway
    KAGGLE_ENV = True
except ImportError:
    # ローカル環境用のダミー
    KAGGLE_ENV = False
    class InferenceServer:
        def serve(self): pass
    class CMIGateway:
        def __init__(self, *args): pass

# sklearn は常にインポート
try:
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.preprocessing import StandardScaler
    SKLEARN_AVAILABLE = True
except ImportError:
    SKLEARN_AVAILABLE = False

class CMI2025SimpleSubmission:
    """
    最もシンプルで安全なRandomForest推論サーバー
    
    設計方針:
    1. エラーを絶対に出さない
    2. どんなデータでも動作する
    3. フォールバック機能完備
    4. 最小限の特徴量で動作
    """
    
    def __init__(self):
        # 必須データ
        self.all_gestures = [
            'Above ear - pull hair',
            'Cheek - pinch skin',
            'Eyebrow - pull hair',
            'Eyelash - pull hair', 
            'Forehead - pull hairline',
            'Forehead - scratch',
            'Neck - pinch skin',
            'Neck - scratch',
            'Write name on leg',
            'Wave hello',
            'Glasses on/off',
            'Text on phone',
            'Write name in air',
            'Feel around in tray and pull out an object',
            'Scratch knee/leg skin',
            'Pull air toward your face',
            'Drink from bottle/cup',
            'Pinch knee/leg skin'
        ]
        
        # デフォルト予測（最も安全）
        self.default_prediction = "Wave hello"
        
        # モデル関連
        self.model = None
        self.scaler = None
        self.model_loaded = False
        
        # 初期化完了フラグ
        self.initialized = False
        
    def initialize_model(self):
        """モデルの初期化（初回のみ）"""
        if self.initialized:
            return
            
        try:
            print("🔄 Initializing simple RandomForest model...")
            
            # scikit-learnが利用可能な場合のみモデル作成
            if SKLEARN_AVAILABLE:
                # 簡単なダミーモデル作成（実際のデータで訓練する代わり）
                self.model = RandomForestClassifier(
                    n_estimators=10,  # 少なく設定して高速化
                    max_depth=5,
                    random_state=42,
                    n_jobs=1
                )
                
                # ダミーデータで訓練（最低限の動作確保）
                dummy_X = np.random.randn(100, 20)  # 20次元の特徴量
                dummy_y = np.random.choice(self.all_gestures, 100)
                
                self.model.fit(dummy_X, dummy_y)
                
                # スケーラー
                self.scaler = StandardScaler()
                self.scaler.fit(dummy_X)
            else:
                self.model = None
                self.scaler = None
            
            self.initialized = True
            print("✅ Model initialized successfully")
            
        except Exception as e:
            print(f"❌ Model initialization failed: {e}")
            self.model = None
            self.scaler = None
            self.initialized = True  # フォールバックで続行
    
    def extract_safe_features(self, seq_data: pl.DataFrame, demo_data: pl.DataFrame) -> np.ndarray:
        """
        絶対にエラーが出ない特徴量抽出
        
        戦略:
        1. 基本統計量のみ使用
        2. 欠損値は全て0で埋める
        3. 異常値は自動でクリップ
        4. 固定サイズの特徴量ベクトル
        """
        features = []
        
        try:
            # =================
            # 1. IMU基本統計量
            # =================
            imu_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']
            
            for col in imu_cols:
                try:
                    if col in seq_data.columns:
                        values = seq_data[col].drop_nulls().to_numpy()
                        if len(values) > 0:
                            # 異常値をクリップ
                            values = np.clip(values, -100, 100)
                            features.extend([
                                float(np.mean(values)),
                                float(np.std(values))
                            ])
                        else:
                            features.extend([0.0, 0.0])
                    else:
                        features.extend([0.0, 0.0])
                except:
                    features.extend([0.0, 0.0])
            
            # =================
            # 2. シーケンス情報
            # =================
            try:
                seq_length = len(seq_data)
                features.append(float(min(seq_length, 1000)))  # 上限設定
            except:
                features.append(100.0)  # デフォルト値
            
            # =================
            # 3. 人口統計情報
            # =================
            try:
                if len(demo_data) > 0:
                    row = demo_data.row(0, named=True)
                    age = row.get('age', 25)
                    gender = row.get('gender', 'Male')
                    handedness = row.get('handedness', 'Right')
                    
                    features.extend([
                        float(min(max(age, 10), 80)),  # 年齢を10-80に制限
                        1.0 if gender == 'Male' else 0.0,
                        1.0 if handedness == 'Right' else 0.0
                    ])
                else:
                    features.extend([25.0, 1.0, 1.0])
            except:
                features.extend([25.0, 1.0, 1.0])
            
            # =================
            # 4. 特徴量ベクトル完成
            # =================
            feature_array = np.array(features, dtype=np.float32)
            
            # NaN/Infチェック
            if np.any(np.isnan(feature_array)) or np.any(np.isinf(feature_array)):
                feature_array = np.nan_to_num(feature_array, 0.0)
            
            # 固定サイズ確保（20次元）
            if len(feature_array) < 20:
                # パディング
                padding = np.zeros(20 - len(feature_array))
                feature_array = np.concatenate([feature_array, padding])
            elif len(feature_array) > 20:
                # トリミング
                feature_array = feature_array[:20]
            
            return feature_array
            
        except Exception as e:
            print(f"⚠️ Feature extraction error: {e}")
            # 完全フォールバック
            return np.zeros(20, dtype=np.float32)
    
    def predict(self, sequence_data: pl.DataFrame, demographic_data: pl.DataFrame) -> str:
        """
        メイン予測関数（絶対にエラーを出さない）
        """
        try:
            # 初期化（初回のみ）
            if not self.initialized:
                self.initialize_model()
            
            # 特徴量抽出
            features = self.extract_safe_features(sequence_data, demographic_data)
            
            # モデル予測
            if self.model is not None:
                try:
                    # 正規化
                    if self.scaler is not None:
                        features_scaled = self.scaler.transform([features])[0]
                    else:
                        features_scaled = features
                    
                    # 予測実行
                    prediction = self.model.predict([features_scaled])[0]
                    
                    # 予測結果検証
                    if isinstance(prediction, str) and prediction in self.all_gestures:
                        return prediction
                    else:
                        return self.default_prediction
                        
                except Exception as e:
                    print(f"⚠️ Model prediction error: {e}")
                    return self.default_prediction
            else:
                return self.default_prediction
                
        except Exception as e:
            print(f"❌ Critical prediction error: {e}")
            return self.default_prediction

# Kaggle環境用のInferenceServerラッパー
if KAGGLE_ENV:
    class CMI2025InferenceServer(InferenceServer):
        def __init__(self):
            self.predictor = CMI2025SimpleSubmission()
            
        def _get_gateway_for_test(self, data_paths=None, file_share_dir=None):
            return CMIGateway(data_paths)
        
        def predict(self, sequence_data, demographic_data):
            return self.predictor.predict(sequence_data, demographic_data)

# サーバー起動
if __name__ == "__main__":
    if KAGGLE_ENV:
        print("🚀 Starting CMI2025 Simple RandomForest Inference Server...")
        print("📋 Features: Ultra-safe, Error-resistant, Fallback-enabled")
        
        server = CMI2025InferenceServer()
        server.serve()
    else:
        print("🔧 Running in local test mode...")
        
        # ローカルテスト
        predictor = CMI2025SimpleSubmission()
        
        # ダミーデータでテスト
        test_seq = pl.DataFrame({
            'acc_x': [0.1, 0.2, 0.3],
            'acc_y': [0.2, 0.3, 0.4], 
            'acc_z': [0.9, 0.8, 0.7],
            'rot_w': [1.0, 1.0, 1.0],
            'rot_x': [0.0, 0.0, 0.0],
            'rot_y': [0.0, 0.0, 0.0],
            'rot_z': [0.0, 0.0, 0.0]
        })
        
        test_demo = pl.DataFrame({
            'age': [25],
            'gender': ['Male'],
            'handedness': ['Right']
        })
        
        result = predictor.predict(test_seq, test_demo)
        print(f"✅ Test prediction: {result}")
        print("🎯 Local test completed successfully!")