
from fantasy_data.tasks import load_fpl_data
import random
SELECTION = {
    "GK": 2,
    "DEF": 5,
    "MID": 5,
    "FWD": 3
}
FORMATIONS = [
    "5-4-1",
    "5-3-2",
    "5-2-3",
    "4-5-1",
    "4-4-2",
    "4-3-3",
    "3-4-3",
    "3-5-2"
]
BUDGET_MIN = 97
BUDGET_MAX = 100
MAX_PLAYERS_PER_TEAM = 3



def validate_team(team):
    """
    Validates overall team constraints:
      - Total price within the budget range.
    """
    total_price = sum(player["Price"] for player in team)
    if total_price < BUDGET_MIN or total_price > BUDGET_MAX:
        return False
    
    return True

def generate_team_greedy_randomized(players, max_attempts=1000, rcl_percent=0.4):
    """
    Generates a team using greedy randomized selection:
      - For each position, sort players by a greedy metric ('form') or (Total points).
      - Create a Restricted Candidate List (RCL) from the top rcl_percent of players.
      - Randomly select players from the RCL while enforcing the maximum team constraint.
      - Validate overall constraints (budget and team composition).
    """
    # Group players by position.
    position_groups = {pos: [] for pos in SELECTION.keys()}
    for p in players:
        if p["Position"] in position_groups:
            position_groups[p["Position"]].append(p)
    
    # Try multiple attempts to form a valid team.
    for attempt in range(max_attempts):
        team = []
        team_counts = {}  # Tracks the count of players per real-life team.
        valid = True
        
        # For each required position, select the needed number of players.
        for pos, count in SELECTION.items():
            candidates = position_groups.get(pos, [])
            # Sort candidates by the 'form' metric in descending order.
            # candidates_sorted = sorted(candidates, key=lambda x: x["Form"], reverse=True)
            # Sort candidates by the 'Total Points' metric in descending order.
            #candidates_sorted = sorted(candidates, key=lambda x: x["Total Points"], reverse=True)

            
            # Sort candidates by the 'form' and 'total points' metric in descending order.
            candidates_sorted = sorted(candidates, key=lambda x: (-x["Total Points"], x["Form"]))
            
            # Build the Restricted Candidate List (RCL): top rcl_percent of candidates.
            rcl_size = max(1, int(len(candidates_sorted) * rcl_percent))
            rcl = candidates_sorted[:rcl_size]
            
            selected_for_position = []
            attempts_for_position = 0
            # Try to select 'count' players for this position.
            while len(selected_for_position) < count and attempts_for_position < 100:
                candidate = random.choice(rcl)
                if not (candidate in selected_for_position): # Not Allowing Duplicates
                    current_team_count = team_counts.get(candidate["Team"], 0)
                    # Check that adding this candidate does not exceed team limits.
                    if current_team_count < MAX_PLAYERS_PER_TEAM:
                        selected_for_position.append(candidate)
                        team_counts[candidate["Team"]] = current_team_count + 1
                attempts_for_position += 1
            
            # If unable to select the required number of players for this position, mark as invalid.
            if len(selected_for_position) < count:
                valid = False
                break
            
            team.extend(selected_for_position)
        
        # Check overall team constraints.
        if valid and validate_team(team):
            return team  # Successfully generated a valid team.
    
    return None  # Failed to generate a valid team after max_attempts.


def create_team_XI(formation,team):
    team_layout = {"XI": [],"Subs" :[]}
    positions = list(SELECTION.keys())
    players_count = [1]
    players_count.extend([int(players_num) for players_num in formation.split('-')])
    players_by_positions = dict(zip(positions,players_count))
    for position , count in players_by_positions.items():
        position_candidates = [player for player in team if player["Position"] == position]
        position_candidates = sorted(position_candidates, key=lambda x: (-x["Total Points"]))
        team_layout["XI"].extend([player for player in position_candidates[:count]])
        team_layout["Subs"].extend([player for player in position_candidates[count:]])
    return team_layout

def suggest_team_formation(team):
    """
    Suggest a team formation using greedy selection:
      - For each position, choose the best players by a greedy metric ('form') or (Total points) to make the best XI for the given foramtion and team.
      - Assign each formation a score Calculated fto the greedy metric
      - Choose The Formation with Heighst score
    """
    score = {}
    heighst_score = 0
    selected_formation = ""
    positions = list(SELECTION.keys())
    for formation in FORMATIONS:
        players_count = [1]
        metric_score = 0
        players_count.extend([int(players_num) for players_num in formation.split('-')])
        players_by_positions = dict(zip(positions,players_count))
        for position , count in players_by_positions.items():
            position_candidates = [player for player in team if player["Position"] == position]
            position_candidates = sorted(position_candidates, key=lambda x: (-x["Total Points"]))
            metric_score += sum([player["Total Points"] for player in position_candidates[:count]])
        score[formation] = metric_score
        if(metric_score > heighst_score):
            heighst_score = metric_score
            selected_formation = formation
    return selected_formation

def get_players_data():
    return load_fpl_data()

def pick_random_team():
    playersList = load_fpl_data()
    team = generate_team_greedy_randomized(playersList)
    return {"Team":team, "Total Team Price": sum(player["Price"] for player in team)}

def pick_team(team):
    formation = suggest_team_formation(team)
    team_layout = create_team_XI(formation,team)
    return {"Formation":formation, "Team":team_layout}
