"""
VYNDR NBA Context Service
Teammate impact, game script, home/road splits, rest/travel, matchup pace,
foul trouble risk, B2B adjustments, positional matchup defense,
usage-efficiency tradeoff. NBA sub-scores endpoint.
"""

import time
import json
import os
import logging
from flask import Blueprint, request, jsonify

from utils.data_warehouse import fetch_with_cache
from utils.archetypes import (
    NBA_DIMENSIONS, DEFAULT_NBA_WEIGHTS, NBA_SUB_SCORES,
    get_archetype_scores, blend_archetype_weights
)

logger = logging.getLogger('vyndr')
nba_context_bp = Blueprint('nba_context', __name__)

NBA_API_DELAY = 0.6

# --- Teammate Impact ---

TEAMMATE_IMPACT_RULES = {
    'primary_ball_handler_out': {
        'remaining_playmaker': {'base_usage_boost': 0.04, 'assist_boost': 1.5},
        'remaining_scorers': {'base_usage_boost': 0.02, 'fg_attempts_boost': 1.8}
    },
    'primary_scorer_out': {
        'secondary_scorers': {'base_usage_boost': 0.05, 'fg_attempts_boost': 2.5},
        'playmaker': {'assist_reduction': -0.8}
    },
    'starting_big_out': {
        'backup_big': {'minutes_boost': 12, 'rebound_boost': 3.0},
        'remaining_bigs': {'rebound_boost': 1.5}
    }
}


def calculate_dynamic_usage_boost(out_player_archetype, beneficiary_profile, base_boost):
    """
    Scale usage boost by beneficiary's headroom. Player at 20% usage has
    more room to absorb than player at 35%.

    Args:
        out_player_archetype: Archetype of the absent player.
        beneficiary_profile: Dict with usage_rate for the beneficiary.
        base_boost: Base usage boost from TEAMMATE_IMPACT_RULES.

    Returns:
        Float — scaled usage boost.
    """
    usage_ceiling = 0.38
    current_usage = beneficiary_profile.get('usage_rate', 0.20)
    headroom = max(0, usage_ceiling - current_usage)
    headroom_factor = min(1.0, headroom / 0.15)
    return round(base_boost * headroom_factor, 3)


def adjust_for_usage_efficiency_tradeoff(usage_boost, player_profile):
    """
    Higher usage often means lower efficiency. ~-1.5% TS per +5% usage increase.
    Without this, model overestimates beneficiaries of teammate absences.

    Args:
        usage_boost: Float usage increase.
        player_profile: Dict with player stats.

    Returns:
        Dict with volume_boost, efficiency_penalty, net_effect.
    """
    ts_penalty_per_5pct_usage = -0.015
    projected_ts_change = usage_boost * (ts_penalty_per_5pct_usage / 0.05)
    return {
        'volume_boost': usage_boost,
        'efficiency_penalty': projected_ts_change,
        'net_effect': usage_boost + projected_ts_change
    }


# --- Game Script ---

def adjust_minutes_for_spread(projected_minutes, spread, is_favorite):
    """
    Adjust projected minutes based on game spread (blowout risk).

    Args:
        projected_minutes: Base projected minutes.
        spread: Point spread (positive number).
        is_favorite: Whether the player's team is favored.

    Returns:
        Adjusted projected minutes.
    """
    if abs(spread) >= 12:
        return projected_minutes * (0.92 if is_favorite else 0.95)
    elif abs(spread) >= 8 and is_favorite:
        return projected_minutes * 0.96
    return projected_minutes


# --- Home/Road Splits ---

def calculate_home_road_adjustment(player_splits, stat_type, is_home_game):
    """
    Apply home/road split as context adjustment. Only when >5% difference.

    Args:
        player_splits: Dict with {stat_type}_home and {stat_type}_road keys.
        stat_type: Stat type string.
        is_home_game: Boolean.

    Returns:
        Float adjustment to projected value.
    """
    home_avg = player_splits.get(f'{stat_type}_home')
    road_avg = player_splits.get(f'{stat_type}_road')
    if home_avg is None or road_avg is None:
        return 0.0
    overall_avg = (home_avg + road_avg) / 2
    if overall_avg == 0:
        return 0.0
    if abs(home_avg - road_avg) / overall_avg < 0.05:
        return 0.0
    return (home_avg if is_home_game else road_avg) - overall_avg


# --- Rest + Travel Fatigue ---

REST_TRAVEL_ADJUSTMENT = {
    'same_timezone': 0.0,
    'one_tz_change': -0.01,
    'two_tz_change': -0.02,
    'three_tz_change': -0.03
}


def calculate_travel_fatigue(prev_game_tz_offset, current_tz_offset):
    """
    Account for travel distance, not just rest days.
    BOS→LAL on a B2B is worse than BOS→NYK on a B2B.

    Args:
        prev_game_tz_offset: UTC offset of previous game arena.
        current_tz_offset: UTC offset of current game arena.

    Returns:
        Float adjustment (negative = fatigue penalty).
    """
    if prev_game_tz_offset is None or current_tz_offset is None:
        return 0.0
    tz_diff = abs(current_tz_offset - prev_game_tz_offset)
    if tz_diff == 0:
        return REST_TRAVEL_ADJUSTMENT['same_timezone']
    elif tz_diff == 1:
        return REST_TRAVEL_ADJUSTMENT['one_tz_change']
    elif tz_diff == 2:
        return REST_TRAVEL_ADJUSTMENT['two_tz_change']
    else:
        return REST_TRAVEL_ADJUSTMENT['three_tz_change']


# --- Matchup-Specific Pace ---

def calculate_matchup_pace(team_a_pace, team_b_pace, league_avg_pace, is_home):
    """
    Matchup-specific pace — not just team averages.
    Two fast teams play FASTER than either team's average.
    Home team pace weighs 60/40.

    Args:
        team_a_pace: Pace of the player's team.
        team_b_pace: Pace of the opponent.
        league_avg_pace: League average pace.
        is_home: Whether the player's team is home.

    Returns:
        Float factor relative to league average (>1.0 = faster).
    """
    if league_avg_pace <= 0:
        return 1.0
    if is_home:
        raw_pace = (team_a_pace * 0.60 + team_b_pace * 0.40)
    else:
        raw_pace = (team_a_pace * 0.40 + team_b_pace * 0.60)
    return raw_pace / league_avg_pace


# --- Foul Trouble Risk ---

def foul_trouble_risk(fouls_per_game):
    """
    Foul-prone players have wider minutes variance.
    Doesn't change the mean — widens the distribution.

    Args:
        fouls_per_game: Season average fouls per game.

    Returns:
        Dict with minutes_std_boost.
    """
    if fouls_per_game >= 3.5:
        return {'minutes_std_boost': 3.0}
    elif fouls_per_game >= 2.8:
        return {'minutes_std_boost': 1.5}
    return {'minutes_std_boost': 0.0}


# --- Stat-Specific B2B Adjustments ---

B2B_ADJUSTMENTS = {
    'points': -0.04,
    'rebounds': 0.02,
    'assists': -0.01,
    'threes': -0.03,
    'pts_reb_ast': -0.02,
    'default': -0.02
}


def apply_b2b_adjustment(projection, stat_type, is_b2b_second_game):
    """
    B2B fatigue is NOT linear across stats.
    Points and threes drop. Rebounds actually increase.

    Args:
        projection: Base projected value.
        stat_type: Stat type string.
        is_b2b_second_game: Whether this is the second game of a B2B.

    Returns:
        Adjusted projection.
    """
    if not is_b2b_second_game:
        return projection
    adj = B2B_ADJUSTMENTS.get(stat_type, B2B_ADJUSTMENTS['default'])
    return projection * (1 + adj)


# --- Positional Matchup Defense ---

def calculate_positional_matchup(position_defenders, team_defensive_rating):
    """
    Position-specific defensive quality, not just team rating.
    When tracking data available, use who actually guarded whom (positionless basketball).

    Args:
        position_defenders: List of defender dicts with 'defensive_rating' and 'minutes'.
        team_defensive_rating: Fallback team-level defensive rating.

    Returns:
        Float defensive rating for this matchup.
    """
    if not position_defenders:
        return team_defensive_rating

    weighted_def = sum(
        p.get('defensive_rating', team_defensive_rating) * p.get('minutes', 20)
        for p in position_defenders
    )
    total_min = sum(p.get('minutes', 20) for p in position_defenders)
    return weighted_def / total_min if total_min > 0 else team_defensive_rating


# --- Playoff Modifiers ---

PLAYOFF_MODIFIERS = {
    'starter_minutes_boost': 1.10,
    'bench_dnp_threshold': 8,
    'primary_scorer_fg_penalty': 0.96,
    'primary_scorer_fta_boost': 1.10,
    'elimination_star_pts_boost': 1.05,
    'elimination_star_min_boost': 1.08,
    'rest_1_day': 'fatigue',
    'rest_4_plus_days': 'rust_flag'
}


def apply_playoff_modifiers(projection, stat_type, game_context, player_profile):
    """
    Apply playoff-specific modifiers to projection.

    Args:
        projection: Base projected value.
        stat_type: Stat type string.
        game_context: Dict with playoff info (is_elimination, is_home, etc.).
        player_profile: Dict with player stats.

    Returns:
        Modified projection.
    """
    if not game_context.get('is_playoff'):
        return projection

    # Starters get more minutes
    if player_profile.get('is_starter'):
        projection *= PLAYOFF_MODIFIERS['starter_minutes_boost']

    # Elimination game — star players elevate
    if game_context.get('is_elimination') and player_profile.get('usage_rate', 0) > 0.25:
        if stat_type == 'points':
            projection *= PLAYOFF_MODIFIERS['elimination_star_pts_boost']

    return projection


# --- NBA Sub-Scores Endpoint ---

@nba_context_bp.route('/sub-scores/<player_id>/<game_id>', methods=['GET'])
def get_nba_sub_scores(player_id, game_id):
    """
    Calculate all NBA sub-scores for a player in a specific game context.
    Returns individual sub-scores that the Node.js engine weights via archetypes.

    Args:
        player_id: NBA player ID.
        game_id: NBA game ID.

    Returns:
        JSON with sub_scores, archetype_scores, and blended_weights.
    """
    stat_type = request.args.get('stat_type', 'points')
    is_home = request.args.get('is_home', 'true').lower() == 'true'

    # Build player profile (would come from nba_api in production)
    player_profile = _get_player_profile_cached(player_id)
    game_context = _get_game_context_cached(game_id)

    # Calculate each sub-score
    recent_form = _calculate_recent_form(player_id, stat_type)
    matchup_defense = _calculate_matchup_defense_score(player_id, game_context)
    pace_factor = _calculate_pace_score(player_profile, game_context, is_home)
    usage_context = _calculate_usage_score(player_id, game_context)
    home_road = calculate_home_road_adjustment(
        player_profile.get('splits', {}), stat_type, is_home
    )
    rest_travel_score = _calculate_rest_travel_score(player_profile, game_context)

    sub_scores = {
        'recent_form': round(recent_form, 3),
        'matchup_defense': round(matchup_defense, 3),
        'pace_factor': round(pace_factor, 3),
        'usage_context': round(usage_context, 3),
        'home_road': round(home_road, 3),
        'rest_travel': round(rest_travel_score, 3)
    }

    archetype_scores = get_archetype_scores(player_profile, NBA_DIMENSIONS)
    blended_weights = blend_archetype_weights(player_profile, NBA_DIMENSIONS, DEFAULT_NBA_WEIGHTS)

    return jsonify({
        'player_id': player_id,
        'game_id': game_id,
        'stat_type': stat_type,
        'sub_scores': sub_scores,
        'archetype_scores': {k: round(v, 3) for k, v in archetype_scores.items()},
        'blended_weights': {k: round(v, 3) for k, v in blended_weights.items()}
    })


@nba_context_bp.route('/teammate-impact/<player_id>/<game_id>', methods=['GET'])
def get_teammate_impact(player_id, game_id):
    """Get teammate impact for a player given tonight's injury report."""
    return jsonify({
        'player_id': player_id,
        'game_id': game_id,
        'impact': {},
        'note': 'Requires live injury report data'
    })


@nba_context_bp.route('/game-script/<game_id>', methods=['GET'])
def get_game_script(game_id):
    """Get game script projections from spread."""
    return jsonify({
        'game_id': game_id,
        'spread': None,
        'minutes_adjustments': {},
        'note': 'Requires odds data'
    })


@nba_context_bp.route('/rest-travel/<player_id>/<game_id>', methods=['GET'])
def get_rest_travel(player_id, game_id):
    """Get rest and travel fatigue for a player."""
    return jsonify({
        'player_id': player_id,
        'game_id': game_id,
        'rest_days': None,
        'travel_fatigue_adj': 0.0,
        'note': 'Requires schedule data'
    })


# --- Internal Helpers ---

def _get_player_profile_cached(player_id):
    """Get or build player profile from cache/API."""
    cached = fetch_with_cache(
        f'nba_profile_{player_id}',
        lambda: _fetch_player_profile(player_id),
        data_type='player_stats'
    )
    return cached or {}


def _fetch_player_profile(player_id):
    """Fetch player profile from nba_api."""
    time.sleep(NBA_API_DELAY)
    try:
        from nba_api.stats.endpoints import CommonPlayerInfo
        info = CommonPlayerInfo(player_id=player_id)
        df = info.get_data_frames()[0]
        if df.empty:
            return {}
        row = df.iloc[0]
        return {
            'player_id': player_id,
            'name': row.get('DISPLAY_FIRST_LAST', ''),
            'team_id': str(row.get('TEAM_ID', '')),
            'position': row.get('POSITION', ''),
            'usage_rate': 0.20,  # populated from team stats
            'assist_rate': 0.15,
            'three_pa_rate': 0.30,
            'fg_pct': 0.45,
            'reb_per_game': 4.0,
            'splits': {}
        }
    except Exception as e:
        logger.warning(f'[VYNDR] Player profile fetch failed: {e}')
        return {}


def _get_game_context_cached(game_id):
    """Get game context from cache."""
    return fetch_with_cache(
        f'nba_game_{game_id}',
        lambda: {'game_id': game_id},
        data_type='player_stats'
    ) or {}


def _calculate_recent_form(player_id, stat_type):
    """Calculate recent form score (0.0-1.0)."""
    return 0.50  # Neutral default; populated with real data via nba_api


def _calculate_matchup_defense_score(player_id, game_context):
    """Calculate matchup defense score (0.0-1.0)."""
    return 0.50


def _calculate_pace_score(player_profile, game_context, is_home):
    """Calculate pace factor score."""
    return 0.50


def _calculate_usage_score(player_id, game_context):
    """Calculate usage context score."""
    return 0.50


def _calculate_rest_travel_score(player_profile, game_context):
    """Calculate rest/travel fatigue score."""
    return 0.0
