"""
CompositeProcessor - Tier 5: COMPOSITE Features (11 columns)

Weighted composite scores based on Tier 1-4 features:
- 8 Radar Scores (0-100): AIM, CLUTCH, PISTOL, DEFENSE, UTILITY, STABILITY, ECONOMY, PACE
- Overall Score (0-100): Weighted sum of 8 dimensions
- Tier Classification: Elite/Advanced/Intermediate/Beginner
- Tier Percentile: Ranking among all players
"""

import sqlite3
from typing import Dict, Any
from .base_processor import BaseFeatureProcessor, NormalizationUtils, SafeAggregator


class CompositeProcessor(BaseFeatureProcessor):
    """Tier 5 COMPOSITE processor - Weighted scores from all previous tiers"""
    
    MIN_MATCHES_REQUIRED = 20  # Need substantial data for reliable composite scores
    
    @staticmethod
    def calculate(steam_id: str, conn_l2: sqlite3.Connection, 
                  pre_features: Dict[str, Any]) -> Dict[str, Any]:
        """
        Calculate all Tier 5 COMPOSITE features (11 columns)
        
        Args:
            steam_id: Player's Steam ID
            conn_l2: L2 database connection
            pre_features: Dictionary containing all Tier 1-4 features
            
        Returns dict with keys starting with 'score_' and 'tier_'
        """
        features = {}
        
        # Check minimum matches
        if not BaseFeatureProcessor.check_min_matches(steam_id, conn_l2, 
                                                       CompositeProcessor.MIN_MATCHES_REQUIRED):
            return _get_default_composite_features()
        
        # Calculate 8 radar dimension scores
        features['score_aim'] = CompositeProcessor._calculate_aim_score(pre_features)
        features['score_clutch'] = CompositeProcessor._calculate_clutch_score(pre_features)
        features['score_pistol'] = CompositeProcessor._calculate_pistol_score(pre_features)
        features['score_defense'] = CompositeProcessor._calculate_defense_score(pre_features)
        features['score_utility'] = CompositeProcessor._calculate_utility_score(pre_features)
        features['score_stability'] = CompositeProcessor._calculate_stability_score(pre_features)
        features['score_economy'] = CompositeProcessor._calculate_economy_score(pre_features)
        features['score_pace'] = CompositeProcessor._calculate_pace_score(pre_features)
        
        # Calculate overall score (Weighted sum of 8 dimensions)
        # Weights: AIM 20%, CLUTCH 12%, PISTOL 10%, DEFENSE 13%, UTILITY 20%, STABILITY 8%, ECONOMY 12%, PACE 5%
        features['score_overall'] = (
            features['score_aim'] * 0.12 +
            features['score_clutch'] * 0.18 +
            features['score_pistol'] * 0.18 +
            features['score_defense'] * 0.20 +
            features['score_utility'] * 0.10 +
            features['score_stability'] * 0.07 +
            features['score_economy'] * 0.08 +
            features['score_pace'] * 0.07
        )
        features['score_overall'] = round(features['score_overall'], 2)
        
        # Classify tier based on overall score
        features['tier_classification'] = CompositeProcessor._classify_tier(features['score_overall'])
        
        # Percentile rank (placeholder - requires all players)
        features['tier_percentile'] = min(features['score_overall'], 100.0)
        
        return features
    
    @staticmethod
    def _calculate_aim_score(features: Dict[str, Any]) -> float:
        """
        AIM Score (0-100) | 20%
        """
        # Extract features
        rating = features.get('core_avg_rating', 0.0)
        kd = features.get('core_avg_kd', 0.0)
        adr = features.get('core_avg_adr', 0.0)
        hs_rate = features.get('core_hs_rate', 0.0)
        multikill_rate = features.get('tac_multikill_rate', 0.0)
        avg_hs = features.get('core_avg_hs_kills', 0.0)
        weapon_div = features.get('core_weapon_diversity', 0.0)
        rifle_hs_rate = features.get('core_rifle_hs_rate', 0.0)
        
        # Normalize (Variable / Baseline * 100)
        rating_score = min((rating / 1.15) * 100, 100)
        kd_score = min((kd / 1.30) * 100, 100)
        adr_score = min((adr / 90) * 100, 100)
        hs_score = min((hs_rate / 0.55) * 100, 100)
        mk_score = min((multikill_rate / 0.22) * 100, 100)
        avg_hs_score = min((avg_hs / 8.5) * 100, 100)
        weapon_div_score = min((weapon_div / 20) * 100, 100)
        rifle_hs_score = min((rifle_hs_rate / 0.50) * 100, 100)
        
        # Weighted Sum
        aim_score = (
            rating_score * 0.15 +
            kd_score * 0.15 +
            adr_score * 0.10 +
            hs_score * 0.15 +
            mk_score * 0.10 +
            avg_hs_score * 0.15 +
            weapon_div_score * 0.10 +
            rifle_hs_score * 0.10
        )
        
        return round(min(max(aim_score, 0), 100), 2)
    
    @staticmethod
    def _calculate_clutch_score(features: Dict[str, Any]) -> float:
        """
        CLUTCH Score (0-100) | 12%
        """
        # Extract features
        # Clutch Score Calculation: (1v1*100 + 1v2*200 + 1v3+*500) / 8
        c1v1 = features.get('tac_clutch_1v1_wins', 0)
        c1v2 = features.get('tac_clutch_1v2_wins', 0)
        c1v3p = features.get('tac_clutch_1v3_plus_wins', 0)
        # Note: tac_clutch_1v3_plus_wins includes 1v3, 1v4, 1v5
        
        raw_clutch_score = (c1v1 * 100 + c1v2 * 200 + c1v3p * 500) / 8.0
        
        comeback_kd = features.get('int_pressure_comeback_kd', 0.0)
        matchpoint_kpr = features.get('int_pressure_matchpoint_kpr', 0.0)
        rating = features.get('core_avg_rating', 0.0)
        
        # 1v3+ Win Rate
        attempts_1v3p = features.get('tac_clutch_1v3_plus_attempts', 0)
        win_1v3p = features.get('tac_clutch_1v3_plus_wins', 0)
        win_rate_1v3p = win_1v3p / attempts_1v3p if attempts_1v3p > 0 else 0.0
        
        clutch_impact = features.get('tac_clutch_impact_score', 0.0)
        
        # Normalize
        clutch_score_val = min((raw_clutch_score / 200) * 100, 100)
        comeback_score = min((comeback_kd / 1.55) * 100, 100)
        matchpoint_score = min((matchpoint_kpr / 0.85) * 100, 100)
        rating_score = min((rating / 1.15) * 100, 100)
        win_rate_1v3p_score = min((win_rate_1v3p / 0.10) * 100, 100)
        clutch_impact_score = min((clutch_impact / 200) * 100, 100)
        
        # Weighted Sum
        final_clutch_score = (
            clutch_score_val * 0.20 +
            comeback_score * 0.25 +
            matchpoint_score * 0.15 +
            rating_score * 0.10 +
            win_rate_1v3p_score * 0.15 +
            clutch_impact_score * 0.15
        )
        
        return round(min(max(final_clutch_score, 0), 100), 2)
    
    @staticmethod
    def _calculate_pistol_score(features: Dict[str, Any]) -> float:
        """
        PISTOL Score (0-100) | 10%
        """
        # Extract features
        fk_rate = features.get('tac_fk_rate', 0.0) # Using general FK rate as per original logic, though user said "手枪局首杀率". 
        # If "手枪局首杀率" means FK rate in pistol rounds specifically, we don't have that in pre-calculated features.
        # Assuming general FK rate or tac_fk_rate is acceptable proxy or that user meant tac_fk_rate.
        # Given "tac_fk_rate" was used in previous Pistol score, I'll stick with it.
        
        pistol_hs_rate = features.get('core_pistol_hs_rate', 0.0)
        entry_win_rate = features.get('tac_opening_duel_winrate', 0.0)
        rating = features.get('core_avg_rating', 0.0)
        smg_kills = features.get('core_smg_kills_total', 0)
        avg_fk = features.get('tac_avg_fk', 0.0)
        
        # Normalize
        fk_score = min((fk_rate / 0.58) * 100, 100) # 58%
        pistol_hs_score = min((pistol_hs_rate / 0.75) * 100, 100) # 75%
        entry_win_score = min((entry_win_rate / 0.47) * 100, 100) # 47%
        rating_score = min((rating / 1.15) * 100, 100)
        smg_score = min((smg_kills / 270) * 100, 100)
        avg_fk_score = min((avg_fk / 3.0) * 100, 100)
        
        # Weighted Sum
        pistol_score = (
            fk_score * 0.20 +
            pistol_hs_score * 0.25 +
            entry_win_score * 0.15 +
            rating_score * 0.10 +
            smg_score * 0.15 +
            avg_fk_score * 0.15
        )
        
        return round(min(max(pistol_score, 0), 100), 2)
    
    @staticmethod
    def _calculate_defense_score(features: Dict[str, Any]) -> float:
        """
        DEFENSE Score (0-100) | 13%
        """
        # Extract features
        ct_rating = features.get('meta_side_ct_rating', 0.0)
        t_rating = features.get('meta_side_t_rating', 0.0)
        ct_kd = features.get('meta_side_ct_kd', 0.0)
        t_kd = features.get('meta_side_t_kd', 0.0)
        ct_kast = features.get('meta_side_ct_kast', 0.0)
        t_kast = features.get('meta_side_t_kast', 0.0)
        
        # Normalize
        ct_rating_score = min((ct_rating / 1.15) * 100, 100)
        t_rating_score = min((t_rating / 1.20) * 100, 100)
        ct_kd_score = min((ct_kd / 1.40) * 100, 100)
        t_kd_score = min((t_kd / 1.45) * 100, 100)
        ct_kast_score = min((ct_kast / 0.70) * 100, 100)
        t_kast_score = min((t_kast / 0.72) * 100, 100)
        
        # Weighted Sum
        defense_score = (
            ct_rating_score * 0.20 +
            t_rating_score * 0.20 +
            ct_kd_score * 0.15 +
            t_kd_score * 0.15 +
            ct_kast_score * 0.15 +
            t_kast_score * 0.15
        )
        
        return round(min(max(defense_score, 0), 100), 2)
    
    @staticmethod
    def _calculate_utility_score(features: Dict[str, Any]) -> float:
        """
        UTILITY Score (0-100) | 20%
        """
        # Extract features
        util_usage = features.get('tac_util_usage_rate', 0.0)
        util_dmg = features.get('tac_util_nade_dmg_per_round', 0.0)
        flash_eff = features.get('tac_util_flash_efficiency', 0.0)
        util_impact = features.get('tac_util_impact_score', 0.0)
        blind = features.get('tac_util_flash_enemies_per_round', 0.0) # 致盲数 (Enemies Blinded per Round)
        flash_rnd = features.get('tac_util_flash_per_round', 0.0)
        flash_ast = features.get('core_avg_flash_assists', 0.0)
        
        # Normalize
        usage_score = min((util_usage / 2.0) * 100, 100)
        dmg_score = min((util_dmg / 4.0) * 100, 100)
        flash_eff_score = min((flash_eff / 1.35) * 100, 100) # 135%
        impact_score = min((util_impact / 22) * 100, 100)
        blind_score = min((blind / 1.0) * 100, 100)
        flash_rnd_score = min((flash_rnd / 0.85) * 100, 100)
        flash_ast_score = min((flash_ast / 2.15) * 100, 100)
        
        # Weighted Sum
        utility_score = (
            usage_score * 0.15 +
            dmg_score * 0.05 +
            flash_eff_score * 0.20 +
            impact_score * 0.20 +
            blind_score * 0.15 +
            flash_rnd_score * 0.15 +
            flash_ast_score * 0.10
        )
        
        return round(min(max(utility_score, 0), 100), 2)
    
    @staticmethod
    def _calculate_stability_score(features: Dict[str, Any]) -> float:
        """
        STABILITY Score (0-100) | 8%
        """
        # Extract features
        volatility = features.get('meta_rating_volatility', 0.0)
        loss_rating = features.get('meta_loss_rating', 0.0)
        consistency = features.get('meta_rating_consistency', 0.0)
        tilt_resilience = features.get('int_pressure_tilt_resistance', 0.0)
        map_stable = features.get('meta_map_stability', 0.0)
        elo_stable = features.get('meta_elo_tier_stability', 0.0)
        recent_form = features.get('meta_recent_form_rating', 0.0)
        
        # Normalize
        # Volatility: Reverse score. 100 - (Vol * 220)
        vol_score = max(0, 100 - (volatility * 220))
        
        loss_score = min((loss_rating / 1.00) * 100, 100)
        cons_score = min((consistency / 70) * 100, 100)
        tilt_score = min((tilt_resilience / 0.80) * 100, 100)
        map_score = min((map_stable / 0.25) * 100, 100)
        elo_score = min((elo_stable / 0.48) * 100, 100)
        recent_score = min((recent_form / 1.15) * 100, 100)
        
        # Weighted Sum
        stability_score = (
            vol_score * 0.20 +
            loss_score * 0.20 +
            cons_score * 0.15 +
            tilt_score * 0.15 +
            map_score * 0.10 +
            elo_score * 0.10 +
            recent_score * 0.10
        )
        
        return round(min(max(stability_score, 0), 100), 2)
    
    @staticmethod
    def _calculate_economy_score(features: Dict[str, Any]) -> float:
        """
        ECONOMY Score (0-100) | 12%
        """
        # Extract features
        dmg_1k = features.get('tac_eco_dmg_per_1k', 0.0)
        eco_kpr = features.get('tac_eco_kpr_eco_rounds', 0.0)
        eco_kd = features.get('tac_eco_kd_eco_rounds', 0.0)
        eco_score = features.get('tac_eco_efficiency_score', 0.0)
        full_kpr = features.get('tac_eco_kpr_full_rounds', 0.0)
        force_win = features.get('tac_eco_force_success_rate', 0.0)
        
        # Normalize
        dmg_score = min((dmg_1k / 19) * 100, 100)
        eco_kpr_score = min((eco_kpr / 0.85) * 100, 100)
        eco_kd_score = min((eco_kd / 1.30) * 100, 100)
        eco_eff_score = min((eco_score / 0.80) * 100, 100)
        full_kpr_score = min((full_kpr / 0.90) * 100, 100)
        force_win_score = min((force_win / 0.50) * 100, 100)
        
        # Weighted Sum
        economy_score = (
            dmg_score * 0.25 +
            eco_kpr_score * 0.20 +
            eco_kd_score * 0.15 +
            eco_eff_score * 0.15 +
            full_kpr_score * 0.15 +
            force_win_score * 0.10
        )
        
        return round(min(max(economy_score, 0), 100), 2)
    
    @staticmethod
    def _calculate_pace_score(features: Dict[str, Any]) -> float:
        """
        PACE Score (0-100) | 5%
        """
        # Extract features
        early_kill_pct = features.get('int_timing_early_kill_share', 0.0)
        aggression = features.get('int_timing_aggression_index', 0.0)
        trade_speed = features.get('int_trade_response_time', 0.0)
        trade_kill = features.get('int_trade_kill_count', 0)
        teamwork = features.get('int_teamwork_score', 0.0)
        first_contact = features.get('int_timing_first_contact_time', 0.0)
        
        # Normalize
        early_score = min((early_kill_pct / 0.44) * 100, 100)
        aggression_score = min((aggression / 1.20) * 100, 100)
        
        # Trade Speed: Reverse score. (2.0 / Trade Speed) * 100
        # Avoid division by zero
        if trade_speed > 0.01:
            trade_speed_score = min((2.0 / trade_speed) * 100, 100)
        else:
            trade_speed_score = 100 # Instant trade
            
        trade_kill_score = min((trade_kill / 650) * 100, 100)
        teamwork_score = min((teamwork / 29) * 100, 100)
        
        # First Contact: Reverse score. (30 / 1st Contact) * 100
        if first_contact > 0.01:
            first_contact_score = min((30 / first_contact) * 100, 100)
        else:
            first_contact_score = 0 # If 0, probably no data, safe to say 0? Or 100?
            # 0 first contact time means instant damage.
            # But "30 / Contact" means smaller contact time gives higher score.
            # If contact time is 0, score explodes.
            # Realistically first contact time is > 0.
            # I will clamp it.
            first_contact_score = 100 # Assume very fast
            
        # Weighted Sum
        pace_score = (
            early_score * 0.25 +
            aggression_score * 0.20 +
            trade_speed_score * 0.20 +
            trade_kill_score * 0.15 +
            teamwork_score * 0.10 +
            first_contact_score * 0.10
        )
        
        return round(min(max(pace_score, 0), 100), 2)
    
    @staticmethod
    def _classify_tier(overall_score: float) -> str:
        """
        Classify player tier based on overall score
        
        Tiers:
        - Elite: 75+
        - Advanced: 60-75
        - Intermediate: 40-60
        - Beginner: <40
        """
        if overall_score >= 75:
            return 'Elite'
        elif overall_score >= 60:
            return 'Advanced'
        elif overall_score >= 40:
            return 'Intermediate'
        else:
            return 'Beginner'


def _get_default_composite_features() -> Dict[str, Any]:
    """Return default zero values for all 11 COMPOSITE features"""
    return {
        'score_aim': 0.0,
        'score_clutch': 0.0,
        'score_pistol': 0.0,
        'score_defense': 0.0,
        'score_utility': 0.0,
        'score_stability': 0.0,
        'score_economy': 0.0,
        'score_pace': 0.0,
        'score_overall': 0.0,
        'tier_classification': 'Beginner',
        'tier_percentile': 0.0,
    }
