use axum::{ extract::{Path, State}, Json, }; use serde::Deserialize; use serde_json::{json, Value}; use crate::{ app::AppState, error::AppResult, services::{club as club_svc, fut_champs as champs_svc, profile as profile_svc, season as season_svc}, }; /// GET /fut-champs — current active session, or null if none. pub async fn get_fut_champs(State(state): State) -> AppResult> { let profile = profile_svc::get_active_profile(&state.pool).await?; let session = champs_svc::get_active_session(&state.pool, &profile.id).await?; Ok(Json(json!({ "session": session, "max_matches": crate::models::fut_champs::MAX_MATCHES, }))) } /// POST /fut-champs/start — open a new FUT Champions week. pub async fn post_start_fut_champs(State(state): State) -> AppResult> { let profile = profile_svc::get_active_profile(&state.pool).await?; let session = champs_svc::start_session(&state.pool, &profile.id).await?; Ok(Json(json!({ "session": session, "message": "FUT Champions week started — play up to 30 matches", }))) } #[derive(Deserialize)] pub struct ChampsMatchRequest { pub goals_for: i64, pub goals_against: i64, } /// POST /fut-champs/:session_id/result — record a match in this session. pub async fn post_champs_result( State(state): State, Path(session_id): Path, Json(req): Json, ) -> AppResult> { let profile = profile_svc::get_active_profile(&state.pool).await?; let session = champs_svc::record_match( &state.pool, &profile.id, &session_id, req.goals_for, req.goals_against, ) .await?; let outcome = if req.goals_for > req.goals_against { "win" } else if req.goals_for == req.goals_against { "draw" } else { "loss" }; Ok(Json(json!({ "session": session, "outcome": outcome, "matches_remaining": session.matches_remaining(), "auto_completed": session.is_complete(), }))) } /// POST /fut-champs/:session_id/claim — claim end-of-week rewards. pub async fn post_claim_champs_rewards( State(state): State, Path(session_id): Path, ) -> AppResult> { let profile = profile_svc::get_active_profile(&state.pool).await?; let club = club_svc::get_club_by_profile(&state.pool, &profile.id).await?; let result = champs_svc::claim_rewards( &state.pool, &profile.id, &session_id, &club.id, &state.pack_defs, ) .await?; Ok(Json(result)) } /// GET /fut-champs/history — past sessions, newest first. pub async fn get_champs_history(State(state): State) -> AppResult> { let profile = profile_svc::get_active_profile(&state.pool).await?; let history = champs_svc::get_history(&state.pool, &profile.id).await?; Ok(Json(json!({ "history": history, "total": history.len(), }))) } /// POST /rivals/claim-weekly — claim weekly Division Rivals reward. pub async fn post_claim_rivals_reward(State(state): State) -> AppResult> { let profile = profile_svc::get_active_profile(&state.pool).await?; let club = club_svc::get_club_by_profile(&state.pool, &profile.id).await?; // Ensure a season row exists season_svc::get_or_create(&state.pool, &profile.id).await?; let result = champs_svc::claim_rivals_reward( &state.pool, &profile.id, &club.id, &state.pack_defs, ) .await?; Ok(Json(result)) }