"""
VYNDR Multi-Dimensional Archetype System
Pitcher, batter, and NBA player archetype detection and weight blending.
ALL dimensions have weight_profiles — without them blending returns defaults.
"""

import logging

logger = logging.getLogger('vyndr')

# ============================================================
# MLB PITCHER DIMENSIONS
# ============================================================

PITCHER_DIMENSIONS = {
    'power': {
        'detect': lambda p: min(1.0, max(0, (p.get('fb_velo_season', 91) - 91) / 6)),
        'weight_profile': {
            'velocity_trend': 0.40, 'command_trend': 0.15,
            'whiff_trend': 0.25, 'pitch_mix_shift': 0.10, 'workload': 0.10
        }
    },
    'finesse': {
        'detect': lambda p: (
            min(1.0, max(0, (p.get('zone_pct_season', 0.42) - 0.42) / 0.10)) *
            min(1.0, max(0, (94 - p.get('fb_velo_season', 94)) / 4))
        ),
        'weight_profile': {
            'velocity_trend': 0.10, 'command_trend': 0.40,
            'whiff_trend': 0.15, 'pitch_mix_shift': 0.25, 'workload': 0.10
        }
    },
    'groundball': {
        'detect': lambda p: min(1.0, max(0, (p.get('gb_rate_season', 0.40) - 0.40) / 0.15)),
        'weight_profile': {
            'velocity_trend': 0.20, 'command_trend': 0.30,
            'whiff_trend': 0.10, 'pitch_mix_shift': 0.25, 'workload': 0.15
        }
    },
    'strikeout_artist': {
        'detect': lambda p: min(1.0, max(0, (p.get('k_rate_season', 0.20) - 0.20) / 0.12)),
        'weight_profile': {
            'velocity_trend': 0.25, 'command_trend': 0.15,
            'whiff_trend': 0.35, 'pitch_mix_shift': 0.15, 'workload': 0.10
        }
    },
    'workhorse': {
        'detect': lambda p: (
            min(1.0, max(0, (p.get('ip_per_start', 5) - 5.0) / 2.0)) *
            min(1.0, max(0, (18 - p.get('pitches_per_ip', 17)) / 4))
        ),
        'weight_profile': {
            'velocity_trend': 0.20, 'command_trend': 0.25,
            'whiff_trend': 0.15, 'pitch_mix_shift': 0.15, 'workload': 0.25
        }
    }
}

DEFAULT_PTI_WEIGHTS = {
    'velocity_trend': 0.30, 'command_trend': 0.25,
    'whiff_trend': 0.20, 'pitch_mix_shift': 0.15, 'workload': 0.10
}

# Pitcher identity tags (binary)
PITCHER_IDENTITY = {
    'putaway_specialist': lambda p: max(p.get('whiff_rates_by_pitch', {}).values(), default=0) > 0.35,
    'pitch_to_contact': lambda p: p.get('k_rate_season', 0.22) < 0.18 and p.get('bb_rate_season', 0.08) < 0.06,
    'max_effort': lambda p: p.get('velo_decay_after_60', 0) > 1.5,
}

# ============================================================
# MLB BATTER DIMENSIONS
# ============================================================

BATTER_DIMENSIONS = {
    'power': {
        'detect': lambda b: min(1.0, max(0, (b.get('avg_exit_velo', 87) - 87) / 6)),
        'weight_profile': {
            'recent_form': 0.20, 'platoon_advantage': 0.15,
            'pitcher_matchup': 0.25, 'park_factor': 0.25, 'lineup_position': 0.15
        }
    },
    'contact': {
        'detect': lambda b: min(1.0, max(0, (0.25 - b.get('k_rate_season', 0.22)) / 0.12)),
        'weight_profile': {
            'recent_form': 0.30, 'platoon_advantage': 0.25,
            'pitcher_matchup': 0.15, 'park_factor': 0.10, 'lineup_position': 0.20
        }
    },
    'speed': {
        'detect': lambda b: min(1.0, max(0, (b.get('sprint_speed', 26) - 26) / 4)),
        'weight_profile': {
            'recent_form': 0.25, 'platoon_advantage': 0.15,
            'pitcher_matchup': 0.15, 'park_factor': 0.10, 'lineup_position': 0.35
        }
    },
    'run_producer': {
        'detect': lambda b: (
            min(1.0, max(0, (b.get('rbi_per_game', 0) - 0.4) / 0.6)) *
            (1.0 if b.get('lineup_position', 9) in [3, 4, 5] else 0.4)
        ),
        'weight_profile': {
            'recent_form': 0.20, 'platoon_advantage': 0.20,
            'pitcher_matchup': 0.20, 'park_factor': 0.15, 'lineup_position': 0.25
        }
    },
    'damage_dealer': {
        'detect': lambda b: min(1.0, max(0, (b.get('iso', 0.140) - 0.140) / 0.120)),
        'weight_profile': {
            'recent_form': 0.20, 'platoon_advantage': 0.15,
            'pitcher_matchup': 0.20, 'park_factor': 0.30, 'lineup_position': 0.15
        }
    }
}

DEFAULT_BCS_WEIGHTS = {
    'recent_form': 0.25, 'platoon_advantage': 0.25,
    'pitcher_matchup': 0.20, 'park_factor': 0.15, 'lineup_position': 0.15
}

# Batter approach tags (binary)
BATTER_APPROACH = {
    'fastball_hunter': lambda b: b.get('fb_whiff_rate', 0.20) < 0.15 and b.get('fb_slg', 0.400) > 0.500,
    'count_worker': lambda b: b.get('bb_rate_season', 0) > 0.10 and b.get('pitches_per_pa', 3.5) > 4.0,
    'first_pitch_aggressive': lambda b: b.get('first_pitch_swing_rate', 0.25) > 0.35,
    'spray_hitter': lambda b: b.get('oppo_pct', 0.20) > 0.25 and b.get('pull_pct', 0.40) < 0.42,
    'situational': lambda b: abs(b.get('risp_ops', 0.750) - b.get('overall_ops', 0.750)) > 0.080,
}

# Batting order context
BATTING_ORDER = {
    1: {'pa_mult': 1.10, 'rbi_ctx': 'low', 'pitch_quality': 'high_fb'},
    2: {'pa_mult': 1.08, 'rbi_ctx': 'moderate', 'pitch_quality': 'high'},
    3: {'pa_mult': 1.05, 'rbi_ctx': 'high', 'pitch_quality': 'mixed'},
    4: {'pa_mult': 1.03, 'rbi_ctx': 'highest', 'pitch_quality': 'mixed'},
    5: {'pa_mult': 1.00, 'rbi_ctx': 'high', 'pitch_quality': 'moderate'},
    6: {'pa_mult': 0.97, 'rbi_ctx': 'moderate', 'pitch_quality': 'moderate'},
    7: {'pa_mult': 0.94, 'rbi_ctx': 'low', 'pitch_quality': 'lower'},
    8: {'pa_mult': 0.91, 'rbi_ctx': 'low', 'pitch_quality': 'lower'},
    9: {'pa_mult': 0.88, 'rbi_ctx': 'lowest', 'pitch_quality': 'varies'}
}

# ============================================================
# NBA DIMENSIONS — ALL with weight_profiles
# ============================================================

NBA_SUB_SCORES = [
    'recent_form', 'matchup_defense', 'pace_factor',
    'usage_context', 'home_road', 'rest_travel'
]

DEFAULT_NBA_WEIGHTS = {
    'recent_form': 0.25, 'matchup_defense': 0.20, 'pace_factor': 0.15,
    'usage_context': 0.20, 'home_road': 0.10, 'rest_travel': 0.10
}

NBA_DIMENSIONS = {
    'primary_scorer': {
        'detect': lambda p: min(1.0, max(0, (p.get('usage_rate', 0.20) - 0.22) / 0.12)),
        'weight_profile': {
            'recent_form': 0.25, 'matchup_defense': 0.30, 'pace_factor': 0.10,
            'usage_context': 0.15, 'home_road': 0.10, 'rest_travel': 0.10
        }
    },
    'primary_playmaker': {
        'detect': lambda p: min(1.0, max(0, (p.get('assist_rate', 0.15) - 0.20) / 0.18)),
        'weight_profile': {
            'recent_form': 0.20, 'matchup_defense': 0.15, 'pace_factor': 0.20,
            'usage_context': 0.30, 'home_road': 0.05, 'rest_travel': 0.10
        }
    },
    'three_and_d': {
        'detect': lambda p: (
            min(1.0, max(0, (p.get('three_pa_rate', 0.30) - 0.35) / 0.25)) *
            min(1.0, max(0, (0.25 - p.get('usage_rate', 0.20)) / 0.08))
        ),
        'weight_profile': {
            'recent_form': 0.30, 'matchup_defense': 0.15, 'pace_factor': 0.15,
            'usage_context': 0.25, 'home_road': 0.10, 'rest_travel': 0.05
        }
    },
    'interior_big': {
        'detect': lambda p: (
            min(1.0, max(0, (p.get('fg_pct', 0.45) - 0.50) / 0.15)) *
            min(1.0, max(0, (p.get('reb_per_game', 4) - 5) / 6))
        ),
        'weight_profile': {
            'recent_form': 0.20, 'matchup_defense': 0.25, 'pace_factor': 0.20,
            'usage_context': 0.15, 'home_road': 0.10, 'rest_travel': 0.10
        }
    },
    'secondary_creator': {
        'detect': lambda p: (
            min(1.0, max(0, (p.get('usage_rate', 0.20) - 0.18) / 0.10)) *
            (1 - min(1.0, max(0, (p.get('usage_rate', 0.20) - 0.28) / 0.05)))
        ),
        'weight_profile': {
            'recent_form': 0.20, 'matchup_defense': 0.15, 'pace_factor': 0.15,
            'usage_context': 0.35, 'home_road': 0.05, 'rest_travel': 0.10
        }
    },
    'stretch_big': {
        'detect': lambda p: (
            min(1.0, max(0, (p.get('reb_per_game', 0) - 5) / 6)) *
            min(1.0, max(0, (p.get('three_pa_rate', 0) - 0.15) / 0.20))
        ),
        'weight_profile': {
            'recent_form': 0.25, 'matchup_defense': 0.20, 'pace_factor': 0.20,
            'usage_context': 0.15, 'home_road': 0.10, 'rest_travel': 0.10
        }
    }
}

# ============================================================
# WEIGHT BLENDING
# ============================================================


def get_archetype_scores(profile, dimensions):
    """
    Calculate archetype scores for a player profile.

    Args:
        profile: Dict of player stats/attributes.
        dimensions: Dict of dimension definitions (e.g., NBA_DIMENSIONS).

    Returns:
        Dict mapping dimension name to detection score (0.0-1.0).
    """
    scores = {}
    for name, dim in dimensions.items():
        try:
            scores[name] = dim['detect'](profile)
        except (KeyError, TypeError, ZeroDivisionError):
            scores[name] = 0.0
    return scores


def blend_archetype_weights(profile, dimensions, defaults):
    """
    Blend weight profiles based on archetype detection scores.
    Returns default weights when all archetype scores are below threshold.

    Args:
        profile: Dict of player stats/attributes.
        dimensions: Dict of dimension definitions.
        defaults: Dict of default weights (fallback).

    Returns:
        Dict of blended weights, proportional to archetype detection scores.
    """
    scores = get_archetype_scores(profile, dimensions)
    total = sum(scores.values())

    if total < 0.1:
        return defaults.copy()

    # Get all weight keys from first dimension's weight_profile
    weight_keys = list(list(dimensions.values())[0].get('weight_profile', defaults).keys())
    blended = {}

    for wk in weight_keys:
        blended[wk] = sum(
            scores[name] * dim.get('weight_profile', defaults).get(wk, 0)
            for name, dim in dimensions.items()
        ) / total

    return blended


def get_batting_order_context(position):
    """
    Get batting order context for a lineup position.

    Args:
        position: Integer lineup position (1-9).

    Returns:
        Dict with pa_mult, rbi_ctx, pitch_quality.
    """
    return BATTING_ORDER.get(position, BATTING_ORDER[9])


def detect_batter_approach(batter_profile):
    """
    Detect batter approach tags (binary classifications).

    Args:
        batter_profile: Dict of batter stats.

    Returns:
        Dict mapping approach tag to bool.
    """
    result = {}
    for tag, detect_fn in BATTER_APPROACH.items():
        try:
            result[tag] = detect_fn(batter_profile)
        except (KeyError, TypeError):
            result[tag] = False
    return result


def detect_pitcher_identity(pitcher_profile):
    """
    Detect pitcher identity tags (binary classifications).

    Args:
        pitcher_profile: Dict of pitcher stats.

    Returns:
        Dict mapping identity tag to bool.
    """
    result = {}
    for tag, detect_fn in PITCHER_IDENTITY.items():
        try:
            result[tag] = detect_fn(pitcher_profile)
        except (KeyError, TypeError):
            result[tag] = False
    return result
