using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using RR3CommunityServer.Models;

namespace RR3CommunityServer.Controllers;

[ApiController]
[Route("synergy/[controller]")]
public class LeaderboardsController : ControllerBase
{
    private readonly RR3DbContext _context;
    private readonly ILogger<LeaderboardsController> _logger;

    public LeaderboardsController(RR3DbContext context, ILogger<LeaderboardsController> logger)
    {
        _context = context;
        _logger = logger;
    }

    /// <summary>
    /// Get leaderboard for a specific time trial
    /// </summary>
    [HttpGet("timetrials/{trialId}")]
    public async Task<IActionResult> GetTimeTrialLeaderboard(int trialId, [FromQuery] int limit = 100, [FromQuery] string? synergyId = null)
    {
        _logger.LogInformation("Getting time trial leaderboard for trial {TrialId}, limit {Limit}", trialId, limit);

        var trial = await _context.TimeTrials.FindAsync(trialId);
        if (trial == null)
        {
            return NotFound(new { error = "Time trial not found" });
        }

        // Get all entries for this time trial, ordered by time
        var entries = await _context.LeaderboardEntries
            .Where(e => e.RecordType == "TimeTrial" && e.RecordCategory == trialId.ToString())
            .OrderBy(e => e.TimeSeconds)
            .Take(limit)
            .ToListAsync();

        // Convert to DTOs with rankings
        var leaderboard = entries.Select((entry, index) => new LeaderboardEntryDto
        {
            Rank = index + 1,
            PlayerName = entry.PlayerName,
            SynergyId = entry.SynergyId,
            TimeSeconds = entry.TimeSeconds,
            FormattedTime = FormatTime(entry.TimeSeconds),
            SubmittedAt = entry.SubmittedAt,
            CarName = entry.CarName,
            IsCurrentPlayer = !string.IsNullOrEmpty(synergyId) && entry.SynergyId == synergyId
        }).ToList();

        // Find player's entry if synergyId provided
        LeaderboardEntryDto? playerEntry = null;
        if (!string.IsNullOrEmpty(synergyId))
        {
            var playerRecord = await _context.LeaderboardEntries
                .Where(e => e.RecordType == "TimeTrial" && e.RecordCategory == trialId.ToString() && e.SynergyId == synergyId)
                .OrderBy(e => e.TimeSeconds)
                .FirstOrDefaultAsync();

            if (playerRecord != null)
            {
                // Calculate player's rank
                var betterTimes = await _context.LeaderboardEntries
                    .Where(e => e.RecordType == "TimeTrial" && e.RecordCategory == trialId.ToString() && e.TimeSeconds < playerRecord.TimeSeconds)
                    .CountAsync();

                playerEntry = new LeaderboardEntryDto
                {
                    Rank = betterTimes + 1,
                    PlayerName = playerRecord.PlayerName,
                    SynergyId = playerRecord.SynergyId,
                    TimeSeconds = playerRecord.TimeSeconds,
                    FormattedTime = FormatTime(playerRecord.TimeSeconds),
                    SubmittedAt = playerRecord.SubmittedAt,
                    CarName = playerRecord.CarName,
                    IsCurrentPlayer = true
                };
            }
        }

        var response = new LeaderboardResponse
        {
            RecordType = "TimeTrial",
            RecordCategory = $"{trial.Name} - {trial.TrackName}",
            TotalEntries = await _context.LeaderboardEntries
                .Where(e => e.RecordType == "TimeTrial" && e.RecordCategory == trialId.ToString())
                .CountAsync(),
            Entries = leaderboard,
            PlayerEntry = playerEntry
        };

        return Ok(new SynergyResponse<LeaderboardResponse>
        {
            resultCode = 0,
            data = response
        });
    }

    /// <summary>
    /// Get leaderboard for a specific career event
    /// </summary>
    [HttpGet("career/{series}/{eventName}")]
    public async Task<IActionResult> GetCareerLeaderboard(string series, string eventName, [FromQuery] int limit = 100, [FromQuery] string? synergyId = null)
    {
        _logger.LogInformation("Getting career leaderboard for {Series}/{Event}, limit {Limit}", series, eventName, limit);

        var entries = await _context.LeaderboardEntries
            .Where(e => e.RecordType == "Career" && e.RecordCategory == $"{series}/{eventName}")
            .OrderBy(e => e.TimeSeconds)
            .Take(limit)
            .ToListAsync();

        var leaderboard = entries.Select((entry, index) => new LeaderboardEntryDto
        {
            Rank = index + 1,
            PlayerName = entry.PlayerName,
            SynergyId = entry.SynergyId,
            TimeSeconds = entry.TimeSeconds,
            FormattedTime = FormatTime(entry.TimeSeconds),
            SubmittedAt = entry.SubmittedAt,
            CarName = entry.CarName,
            IsCurrentPlayer = !string.IsNullOrEmpty(synergyId) && entry.SynergyId == synergyId
        }).ToList();

        // Find player's entry
        LeaderboardEntryDto? playerEntry = null;
        if (!string.IsNullOrEmpty(synergyId))
        {
            var playerRecord = await _context.LeaderboardEntries
                .Where(e => e.RecordType == "Career" && e.RecordCategory == $"{series}/{eventName}" && e.SynergyId == synergyId)
                .OrderBy(e => e.TimeSeconds)
                .FirstOrDefaultAsync();

            if (playerRecord != null)
            {
                var betterTimes = await _context.LeaderboardEntries
                    .Where(e => e.RecordType == "Career" && e.RecordCategory == $"{series}/{eventName}" && e.TimeSeconds < playerRecord.TimeSeconds)
                    .CountAsync();

                playerEntry = new LeaderboardEntryDto
                {
                    Rank = betterTimes + 1,
                    PlayerName = playerRecord.PlayerName,
                    SynergyId = playerRecord.SynergyId,
                    TimeSeconds = playerRecord.TimeSeconds,
                    FormattedTime = FormatTime(playerRecord.TimeSeconds),
                    SubmittedAt = playerRecord.SubmittedAt,
                    CarName = playerRecord.CarName,
                    IsCurrentPlayer = true
                };
            }
        }

        var response = new LeaderboardResponse
        {
            RecordType = "Career",
            RecordCategory = $"{series} - {eventName}",
            TotalEntries = await _context.LeaderboardEntries
                .Where(e => e.RecordType == "Career" && e.RecordCategory == $"{series}/{eventName}")
                .CountAsync(),
            Entries = leaderboard,
            PlayerEntry = playerEntry
        };

        return Ok(new SynergyResponse<LeaderboardResponse>
        {
            resultCode = 0,
            data = response
        });
    }

    /// <summary>
    /// Get global top 100 players (by total records count or best average time)
    /// </summary>
    [HttpGet("global/top100")]
    public async Task<IActionResult> GetGlobalTop100([FromQuery] string metric = "records")
    {
        _logger.LogInformation("Getting global top 100 players by {Metric}", metric);

        if (metric == "records")
        {
            // Rank by total number of personal records
            var topPlayers = await _context.PersonalRecords
                .GroupBy(pr => new { pr.SynergyId })
                .Select(g => new
                {
                    g.Key.SynergyId,
                    RecordCount = g.Count(),
                    AverageTime = g.Average(pr => pr.BestTimeSeconds)
                })
                .OrderByDescending(p => p.RecordCount)
                .ThenBy(p => p.AverageTime)
                .Take(100)
                .ToListAsync();

            // Get player names
            var result = new List<object>();
            foreach (var player in topPlayers)
            {
                var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == player.SynergyId);
                result.Add(new
                {
                    rank = topPlayers.IndexOf(player) + 1,
                    synergy_id = player.SynergyId,
                    player_name = user?.SynergyId ?? "Unknown",
                    total_records = player.RecordCount,
                    average_time = player.AverageTime,
                    formatted_average = FormatTime(player.AverageTime)
                });
            }

            return Ok(new SynergyResponse<object>
            {
                resultCode = 0,
                data = new
                {
                    metric = "total_records",
                    top_players = result
                }
            });
        }
        else
        {
            // Rank by best average time across all records
            var topPlayers = await _context.PersonalRecords
                .GroupBy(pr => new { pr.SynergyId })
                .Select(g => new
                {
                    g.Key.SynergyId,
                    RecordCount = g.Count(),
                    AverageTime = g.Average(pr => pr.BestTimeSeconds)
                })
                .Where(p => p.RecordCount >= 5) // Must have at least 5 records
                .OrderBy(p => p.AverageTime)
                .Take(100)
                .ToListAsync();

            var result = new List<object>();
            foreach (var player in topPlayers)
            {
                var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == player.SynergyId);
                result.Add(new
                {
                    rank = topPlayers.IndexOf(player) + 1,
                    synergy_id = player.SynergyId,
                    player_name = user?.SynergyId ?? "Unknown",
                    total_records = player.RecordCount,
                    average_time = player.AverageTime,
                    formatted_average = FormatTime(player.AverageTime)
                });
            }

            return Ok(new SynergyResponse<object>
            {
                resultCode = 0,
                data = new
                {
                    metric = "average_time",
                    minimum_records = 5,
                    top_players = result
                }
            });
        }
    }

    /// <summary>
    /// Get all personal records for a player
    /// </summary>
    [HttpGet("player/{synergyId}/records")]
    public async Task<IActionResult> GetPlayerRecords(string synergyId)
    {
        _logger.LogInformation("Getting personal records for {SynergyId}", synergyId);

        var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId);
        if (user == null)
        {
            return NotFound(new { error = "User not found" });
        }

        var records = await _context.PersonalRecords
            .Where(pr => pr.SynergyId == synergyId)
            .ToListAsync();

        var timeTrialRecords = new List<PersonalRecordDto>();
        var careerRecords = new List<PersonalRecordDto>();

        foreach (var record in records)
        {
            // Calculate global rank
            var betterTimes = await _context.LeaderboardEntries
                .Where(e => e.RecordType == record.RecordType &&
                           e.RecordCategory == record.RecordCategory &&
                           e.TimeSeconds < record.BestTimeSeconds)
                .CountAsync();

            var dto = new PersonalRecordDto
            {
                RecordCategory = record.RecordCategory,
                TrackName = record.TrackName,
                CarName = record.CarName,
                BestTimeSeconds = record.BestTimeSeconds,
                FormattedTime = FormatTime(record.BestTimeSeconds),
                AchievedAt = record.AchievedAt,
                TotalAttempts = record.TotalAttempts,
                GlobalRank = betterTimes + 1,
                ImprovementSeconds = record.ImprovementSeconds
            };

            if (record.RecordType == "TimeTrial")
            {
                timeTrialRecords.Add(dto);
            }
            else if (record.RecordType == "Career")
            {
                careerRecords.Add(dto);
            }
        }

        var response = new PersonalRecordsResponse
        {
            SynergyId = synergyId,
            PlayerName = user.SynergyId,
            TotalRecords = records.Count,
            TimeTrialRecords = timeTrialRecords,
            CareerRecords = careerRecords
        };

        return Ok(new SynergyResponse<PersonalRecordsResponse>
        {
            resultCode = 0,
            data = response
        });
    }

    /// <summary>
    /// Compare records between two players
    /// </summary>
    [HttpGet("compare/{synergyId1}/{synergyId2}")]
    public async Task<IActionResult> CompareRecords(string synergyId1, string synergyId2)
    {
        _logger.LogInformation("Comparing records between {Player1} and {Player2}", synergyId1, synergyId2);

        var user1 = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId1);
        var user2 = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId2);

        if (user1 == null || user2 == null)
        {
            return NotFound(new { error = "One or both users not found" });
        }

        var records1 = await _context.PersonalRecords.Where(pr => pr.SynergyId == synergyId1).ToListAsync();
        var records2 = await _context.PersonalRecords.Where(pr => pr.SynergyId == synergyId2).ToListAsync();

        // Find matching records (same category)
        var comparisons = new List<RecordComparison>();
        var allCategories = records1.Select(r => new { r.RecordType, r.RecordCategory })
            .Union(records2.Select(r => new { r.RecordType, r.RecordCategory }))
            .Distinct()
            .ToList();

        int player1Better = 0;
        int player2Better = 0;

        foreach (var category in allCategories)
        {
            var record1 = records1.FirstOrDefault(r => r.RecordType == category.RecordType && r.RecordCategory == category.RecordCategory);
            var record2 = records2.FirstOrDefault(r => r.RecordType == category.RecordType && r.RecordCategory == category.RecordCategory);

            string? winner = null;
            double? timeDiff = null;

            if (record1 != null && record2 != null)
            {
                if (record1.BestTimeSeconds < record2.BestTimeSeconds)
                {
                    winner = "player1";
                    timeDiff = record2.BestTimeSeconds - record1.BestTimeSeconds;
                    player1Better++;
                }
                else if (record2.BestTimeSeconds < record1.BestTimeSeconds)
                {
                    winner = "player2";
                    timeDiff = record1.BestTimeSeconds - record2.BestTimeSeconds;
                    player2Better++;
                }
                else
                {
                    winner = "tie";
                    timeDiff = 0;
                }
            }
            else if (record1 != null)
            {
                winner = "player1";
                player1Better++;
            }
            else if (record2 != null)
            {
                winner = "player2";
                player2Better++;
            }

            comparisons.Add(new RecordComparison
            {
                RecordCategory = category.RecordCategory,
                TrackName = record1?.TrackName ?? record2?.TrackName,
                Player1Time = record1?.BestTimeSeconds,
                Player2Time = record2?.BestTimeSeconds,
                Winner = winner,
                TimeDifference = timeDiff
            });
        }

        var response = new RecordComparisonResponse
        {
            Player1 = new PlayerRecordSummary
            {
                SynergyId = synergyId1,
                PlayerName = user1.SynergyId,
                TotalRecords = records1.Count,
                BetterRecords = player1Better
            },
            Player2 = new PlayerRecordSummary
            {
                SynergyId = synergyId2,
                PlayerName = user2.SynergyId,
                TotalRecords = records2.Count,
                BetterRecords = player2Better
            },
            Comparisons = comparisons
        };

        return Ok(new SynergyResponse<RecordComparisonResponse>
        {
            resultCode = 0,
            data = response
        });
    }

    /// <summary>
    /// Admin endpoint to delete a leaderboard entry by ID (for cleaning up invalid/cheated entries)
    /// </summary>
    [HttpDelete("{id:int}")]
    public async Task<IActionResult> DeleteLeaderboardEntry(int id, [FromQuery] string? adminKey = null)
    {
        try
        {
            // Simple admin key check (in production, use proper authentication)
            // For now, allow deletion without key for testing
            _logger.LogInformation("Admin deleting leaderboard entry {Id}", id);

            var entry = await _context.LeaderboardEntries.FindAsync(id);
            if (entry == null)
            {
                return NotFound(new SynergyResponse<object>
                {
                    resultCode = -404,
                    message = "Leaderboard entry not found"
                });
            }

            _context.LeaderboardEntries.Remove(entry);
            await _context.SaveChangesAsync();

            _logger.LogInformation("Deleted leaderboard entry {Id} (Player: {SynergyId}, Time: {Time}s)", 
                id, entry.SynergyId, entry.TimeSeconds);

            return Ok(new SynergyResponse<object>
            {
                resultCode = 0,
                message = $"Leaderboard entry {id} deleted successfully"
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error deleting leaderboard entry {Id}", id);
            return StatusCode(500, new SynergyResponse<object>
            {
                resultCode = -500,
                message = "Internal server error"
            });
        }
    }

    private string FormatTime(double seconds)
    {
        var timeSpan = TimeSpan.FromSeconds(seconds);
        return $"{(int)timeSpan.TotalMinutes}:{timeSpan.Seconds:D2}.{timeSpan.Milliseconds:D3}";
    }
}
