using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using RR3CommunityServer.Data; using RR3CommunityServer.Services; using static RR3CommunityServer.Data.RR3DbContext; namespace RR3CommunityServer.Controllers; [ApiController] [Route("synergy/[controller]")] public class RewardsController : ControllerBase { private readonly RR3DbContext _context; private readonly ILogger _logger; public RewardsController(RR3DbContext context, ILogger logger) { _context = context; _logger = logger; } /// /// Get daily reward status for a user /// [HttpGet("daily/{synergyId}")] public async Task GetDailyReward(string synergyId) { _logger.LogInformation("Getting daily reward for {SynergyId}", synergyId); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return NotFound(new { error = "User not found" }); } var today = DateTime.UtcNow.Date; var reward = await _context.DailyRewards .FirstOrDefaultAsync(r => r.UserId == user.Id && r.RewardDate.Date == today); if (reward == null) { // Create new daily reward reward = new DailyReward { UserId = user.Id, RewardDate = DateTime.UtcNow, GoldAmount = 50, // Daily gold reward CashAmount = 5000, // Daily cash reward Claimed = false, Streak = await CalculateStreak(user.Id) }; _context.DailyRewards.Add(reward); await _context.SaveChangesAsync(); } return Ok(new { available = !reward.Claimed, gold = reward.GoldAmount, cash = reward.CashAmount, streak = reward.Streak, nextRewardIn = reward.Claimed ? (24 - (DateTime.UtcNow - reward.RewardDate).TotalHours) : 0 }); } /// /// Claim daily reward /// [HttpPost("daily/{synergyId}/claim")] public async Task ClaimDailyReward(string synergyId) { _logger.LogInformation("Claiming daily reward for {SynergyId}", synergyId); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return NotFound(new { error = "User not found" }); } var today = DateTime.UtcNow.Date; var reward = await _context.DailyRewards .FirstOrDefaultAsync(r => r.UserId == user.Id && r.RewardDate.Date == today); if (reward == null || reward.Claimed) { return BadRequest(new { error = "No reward available to claim" }); } // Mark as claimed reward.Claimed = true; reward.ClaimedAt = DateTime.UtcNow; // Update user's gold and cash if (user.Gold == null) user.Gold = 0; if (user.Cash == null) user.Cash = 0; user.Gold += reward.GoldAmount; user.Cash += reward.CashAmount; await _context.SaveChangesAsync(); return Ok(new { success = true, goldEarned = reward.GoldAmount, cashEarned = reward.CashAmount, totalGold = user.Gold, totalCash = user.Cash, streak = reward.Streak }); } /// /// Purchase gold with real money (free in community server) /// /// /// Purchase gold - FREE in community server per EA agreement /// IMPORTANT: No real money transactions allowed! /// [HttpPost("gold/purchase")] public async Task PurchaseGold([FromBody] GoldPurchaseRequest request) { _logger.LogInformation("Processing gold purchase for {SynergyId}: {Amount} gold (FREE - community server)", request.SynergyId, request.GoldAmount); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId); if (user == null) { return NotFound(new { error = "User not found" }); } // ⚖️ EA COMPLIANCE: All gold purchases are FREE in community server! // Per EA agreement: No real money in-app purchases allowed if (user.Gold == null) user.Gold = 0; user.Gold += request.GoldAmount; // Log the purchase (for tracking, not billing) var purchase = new Purchase { SynergyId = request.SynergyId, UserId = user.Id, ItemId = $"gold_{request.GoldAmount}", Sku = request.Sku ?? $"com.ea.rr3.gold_{request.GoldAmount}", OrderId = Guid.NewGuid().ToString(), PurchaseTime = DateTime.UtcNow, Token = Guid.NewGuid().ToString(), Price = 0, // ⚖️ MUST BE 0 - EA COMPLIANCE Status = "approved" }; _context.Purchases.Add(purchase); await _context.SaveChangesAsync(); return Ok(new { success = true, goldPurchased = request.GoldAmount, totalGold = user.Gold, orderId = purchase.OrderId, message = "Gold added to your account (FREE in community server!)" }); } /// /// Get time trial events /// [HttpGet("timetrials")] public async Task GetTimeTrials() { _logger.LogInformation("Getting time trial events"); var trials = await _context.TimeTrials .Where(t => t.Active) .OrderBy(t => t.StartDate) .ToListAsync(); return Ok(new { events = trials.Select(t => new { id = t.Id, name = t.Name, track = t.TrackName, car = t.CarName, startDate = t.StartDate, endDate = t.EndDate, goldReward = t.GoldReward, cashReward = t.CashReward, targetTime = t.TargetTime }) }); } /// /// Submit time trial result /// [HttpPost("timetrials/{trialId}/submit")] public async Task SubmitTimeTrial(int trialId, [FromBody] TimeTrialSubmission submission) { _logger.LogInformation("Submitting time trial {TrialId} for {SynergyId}: {Time}s", trialId, submission.SynergyId, submission.TimeSeconds); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == submission.SynergyId); if (user == null) { return NotFound(new { error = "User not found" }); } var trial = await _context.TimeTrials.FindAsync(trialId); if (trial == null || !trial.Active) { return NotFound(new { error = "Time trial not found or inactive" }); } // Check if beat target time bool beatTarget = submission.TimeSeconds <= trial.TargetTime; int goldEarned = beatTarget ? trial.GoldReward : 0; int cashEarned = beatTarget ? trial.CashReward : trial.CashReward / 2; // Half cash for participation // Save result var result = new TimeTrialResult { UserId = user.Id, TimeTrialId = trialId, TimeSeconds = submission.TimeSeconds, SubmittedAt = DateTime.UtcNow, BeatTarget = beatTarget, GoldEarned = goldEarned, CashEarned = cashEarned }; _context.TimeTrialResults.Add(result); // Award currency if (user.Gold == null) user.Gold = 0; if (user.Cash == null) user.Cash = 0; user.Gold += goldEarned; user.Cash += cashEarned; await _context.SaveChangesAsync(); return Ok(new { success = true, beatTarget = beatTarget, timeSeconds = submission.TimeSeconds, targetTime = trial.TargetTime, goldEarned = goldEarned, cashEarned = cashEarned, totalGold = user.Gold, totalCash = user.Cash, message = beatTarget ? "🏆 Target time beaten!" : "Good try! Keep racing!" }); } private async Task CalculateStreak(int userId) { var rewards = await _context.DailyRewards .Where(r => r.UserId == userId && r.Claimed) .OrderByDescending(r => r.RewardDate) .ToListAsync(); int streak = 0; var currentDate = DateTime.UtcNow.Date; foreach (var reward in rewards) { if (reward.RewardDate.Date == currentDate.AddDays(-streak)) { streak++; } else { break; } } return streak; } } public class GoldPurchaseRequest { public string SynergyId { get; set; } = string.Empty; public int GoldAmount { get; set; } public string? Sku { get; set; } } public class TimeTrialSubmission { public string SynergyId { get; set; } = string.Empty; public double TimeSeconds { get; set; } }