File size: 10,602 Bytes
c6abe34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
"""
Player management API endpoints.
"""
from uuid import uuid4
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status

from app.dependencies import get_current_user, get_supabase
from app.models.user import AccountType
from app.models.player import (
    PlayerCreate,
    PlayerUpdate,
    Player,
    PlayerWithStats,
    PlayerListResponse,
)
from app.services.supabase_client import SupabaseService


router = APIRouter()


@router.post("", response_model=Player, status_code=status.HTTP_201_CREATED)
async def create_player(
    player_data: PlayerCreate,
    current_user: dict = Depends(get_current_user),
    supabase: SupabaseService = Depends(get_supabase),
):
    """
    Create a new player profile.
    
    - **TEAM accounts**: Must provide organization_id
    - **PERSONAL accounts**: Player is linked to user account
    """
    player_id = str(uuid4())
    
    player_record = {
        "id": player_id,
        "name": player_data.name,
        "jersey_number": player_data.jersey_number,
        "position": player_data.position,
        "height_cm": player_data.height_cm,
        "weight_kg": player_data.weight_kg,
        "date_of_birth": str(player_data.date_of_birth) if player_data.date_of_birth else None,
    }
    
    # Handle based on account type
    if current_user.get("account_type") == AccountType.TEAM.value:
        if not player_data.organization_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="organization_id is required for TEAM accounts"
            )
        
        # Verify org ownership
        org = await supabase.select_one("organizations", str(player_data.organization_id))
        if not org or org["owner_id"] != current_user["id"]:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="You don't have access to this organization"
            )
        
        player_record["organization_id"] = str(player_data.organization_id)
    else:
        # PERSONAL account - link to user
        player_record["user_id"] = current_user["id"]
    
    await supabase.insert("players", player_record)
    
    return Player(**player_record, created_at=datetime.utcnow())


@router.get("", response_model=PlayerListResponse)
async def list_players(
    organization_id: Optional[str] = Query(None),
    current_user: dict = Depends(get_current_user),
    supabase: SupabaseService = Depends(get_supabase),
):
    """
    List players.
    
    - **TEAM accounts**: Filter by organization_id
    - **PERSONAL accounts**: Returns the user's player profile
    """
    if current_user.get("account_type") == AccountType.TEAM.value:
        if not organization_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="organization_id query parameter is required for TEAM accounts"
            )
        
        # Verify org ownership
        org = await supabase.select_one("organizations", organization_id)
        if not org or org["owner_id"] != current_user["id"]:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="You don't have access to this organization"
            )
        
        players = await supabase.select(
            "players",
            filters={"organization_id": organization_id},
            order_by="name",
        )
    else:
        # PERSONAL account
        players = await supabase.select(
            "players",
            filters={"user_id": current_user["id"]},
        )
    
    return PlayerListResponse(
        players=[Player(**p) for p in players],
        total=len(players),
    )


@router.get("/{player_id}", response_model=PlayerWithStats)
async def get_player(
    player_id: str,
    current_user: dict = Depends(get_current_user),
    supabase: SupabaseService = Depends(get_supabase),
):
    """
    Get player details with statistics.
    """
    player = await supabase.select_one("players", player_id)
    
    if not player:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Player not found"
        )
    
    # Verify access
    has_access = False
    if current_user.get("account_type") == AccountType.TEAM.value:
        if player.get("organization_id"):
            org = await supabase.select_one("organizations", str(player["organization_id"]))
            has_access = org and str(org.get("owner_id")) == str(current_user["id"])
    elif current_user.get("account_type") == AccountType.COACH.value:
        player_org = player.get("organization_id")
        coach_org = current_user.get("organization_id")
        has_access = player_org and coach_org and str(player_org) == str(coach_org)
    else:
        has_access = player.get("user_id") == current_user["id"]
    
    if not has_access:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You don't have access to this player"
        )
    
    # Data Fallback: If this is an organization-linked record, try to fill missing fields from personal profile
    if player.get("organization_id") and player.get("user_id"):
        try:
            # Get all profiles for this user
            all_user_profiles = await supabase.select("players", filters={"user_id": str(player["user_id"])})
            # Find the personal one (no organization_id)
            personal = next((p for p in all_user_profiles if not p.get("organization_id")), None)
            
            if personal:
                # Fields to potentially fallback to
                fallback_fields = [
                    "jersey_number", "position", "height_cm", "weight_kg", 
                    "date_of_birth", "avatar_url", "phone", "address", 
                    "experience_years", "bio", "status"
                ]
                for field in fallback_fields:
                    # Use personal data if team-specific data is missing or empty
                    val = player.get(field)
                    pers_val = personal.get(field)
                    if (val is None or val == "") and (pers_val is not None and pers_val != ""):
                        player[field] = pers_val
        except Exception as e:
            print(f"Warning: Failed to fetch personal profile fallback for player {player_id}: {e}")
            
    # Ensure ppg exists and is a float
    p_ppg = player.get("ppg")
    player["ppg"] = float(p_ppg) if p_ppg is not None and p_ppg != "" else 0.0
    
    # Coerce string fields that might have been stored as numbers in the DB
    if player.get("experience_years") is not None:
        player["experience_years"] = str(player["experience_years"])
    
    # Fetch email if linked to a user
    email = None
    if player.get("user_id"):
        user = await supabase.select_one("users", str(player["user_id"]))
        if user:
            email = user.get("email")
    
    # Get stats from analytics (video-based tracked metrics)
    analytics_data = await supabase.select("analytics", filters={"player_id": player_id})
    
    video_dist = sum(a.get("value", 0) for a in analytics_data if a.get("metric_type") == "distance_km")
    speed_values = [a.get("value", 0) for a in analytics_data if a.get("metric_type") == "avg_speed_kmh"]
    avg_speed = sum(speed_values) / len(speed_values) if speed_values else None
    
    # Calculate unique videos from tracking
    tracking_video_ids = set(a.get("video_id") for a in analytics_data if a.get("video_id"))
    
    # NEW: Fetch official match stats (linked from box score uploads)
    match_stats = await supabase.select("match_player_stats", filters={"player_profile_id": player_id})
    
    total_pts = sum(s.get("pts", 0) for s in match_stats)
    total_matches = len(match_stats)
    
    # Calculate PPG based on official match stats if available, otherwise fallback to existing
    if total_matches > 0:
        player["ppg"] = round(total_pts / total_matches, 1)
        # We can also add more aggregated stats to the model if needed in the future
    
    # Combine unique videos from both sources
    match_video_ids = set(s.get("match_id") for s in match_stats if s.get("match_id"))
    total_unique_videos = len(tracking_video_ids.union(match_video_ids))

    # Build the final response dict
    player["email"] = email
    player["total_videos"] = total_unique_videos
    player["total_training_minutes"] = total_unique_videos * 40.0 # Estimate 40m per game/video
    player["total_distance_km"] = video_dist if video_dist > 0 else None
    player["avg_speed_kmh"] = avg_speed
    
    return PlayerWithStats(**player)


@router.put("/{player_id}", response_model=Player)
async def update_player(
    player_id: str,
    update_data: PlayerUpdate,
    current_user: dict = Depends(get_current_user),
    supabase: SupabaseService = Depends(get_supabase),
):
    """
    Update player profile.
    """
    player = await supabase.select_one("players", player_id)
    
    if not player:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Player not found"
        )
    
    # Verify access
    has_access = False
    if current_user.get("account_type") == AccountType.TEAM.value:
        if player.get("organization_id"):
            org = await supabase.select_one("organizations", player["organization_id"])
            has_access = org and org["owner_id"] == current_user["id"]
    elif current_user.get("account_type") == AccountType.COACH.value:
        player_org = player.get("organization_id")
        coach_org = current_user.get("organization_id")
        has_access = player_org and coach_org and str(player_org) == str(coach_org)
    else:
        has_access = player.get("user_id") == current_user["id"]
    
    if not has_access:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You don't have permission to update this player"
        )
    
    update_dict = update_data.model_dump(exclude_unset=True)
    
    if "date_of_birth" in update_dict and update_dict["date_of_birth"]:
        update_dict["date_of_birth"] = str(update_dict["date_of_birth"])
    
    if not update_dict:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No fields to update"
        )
    
    updated = await supabase.update("players", player_id, update_dict)
    
    return Player(**updated)