using System;
using Enyim.Caching;
using Expedience.Infrastructure.Models;
using Expedience.Models;
using Microsoft.EntityFrameworkCore;

namespace Expedience.Infrastructure;

public interface IExpedienceRepository
{
	ValueTask<List<DutyCompletionRecord>> GetDutyCompletionRecords(string expac, CancellationToken cancellationToken);
    ValueTask<List<DeepDungeonRecord>> GetDeepDungeonRecords(CancellationToken cancellationToken);
    ValueTask<List<ContentTypeRecord>> GetContentTypeRecords(string contentType, CancellationToken cancellationToken);
    ValueTask<List<TopXCompletionResultDto>> GetTopXRecords(int recordType, int territoryId, int limit, CancellationToken cancellationToken);
}

public class ExpedienceRepository : IExpedienceRepository
{
    private readonly ExpedienceContext _dbContext;
    private readonly IMemcachedClient _memcachedClient;

    public ExpedienceRepository(ExpedienceContext dbContext, IMemcachedClient memcachedClient)
    {
        _dbContext = dbContext;
        _memcachedClient = memcachedClient;
    }

    public async ValueTask<List<DutyCompletionRecord>> GetDutyCompletionRecords(string expac, CancellationToken cancellationToken)
    {
        var cacheKey = $"xpd-dcr-{expac}";
        var cacheSeconds = 600;
        var records = await _memcachedClient.GetValueOrCreateAsync(cacheKey, cacheSeconds,
            async () => await _dbContext.DutyCompletionRecords.FromSqlInterpolated($"SELECT * FROM public.get_dutycompletionrecords({expac})")
                .ToListAsync(cancellationToken));

        return records;
    }

    public async ValueTask<List<DeepDungeonRecord>> GetDeepDungeonRecords(CancellationToken cancellationToken)
    {
        var cacheKey = $"xpd-dd";
        var cacheSeconds = 600;
        var records = await _memcachedClient.GetValueOrCreateAsync(cacheKey, cacheSeconds,
            async () => await _dbContext.DeepDungeonRecords.FromSqlInterpolated($"SELECT * FROM public.get_deepdungeonresults()")
                .ToListAsync(cancellationToken));

        return records;
    }

    public async ValueTask<List<TopXCompletionResultDto>> GetTopXRecords(int recordType, int territoryId, int limit, CancellationToken cancellationToken)
    {
        var cacheKey = $"xpd-topx-{recordType}-{territoryId}-{limit}";

        var cacheSeconds = 600;
        var records = await _memcachedClient.GetValueOrCreateAsync(cacheKey, cacheSeconds,
            async () =>
            {
                var topXResults = await _dbContext.TopXRecords.FromSqlInterpolated($"SELECT * FROM public.get_topxcompletionresults({recordType}, {territoryId}, {limit})")
                    .ToListAsync(cancellationToken);

                if (topXResults.Count == 0) return new List<TopXCompletionResultDto>();

                var topXIds = topXResults.Select(r => r.Id).ToList();
                var userIds = topXResults.Select(r => r.UserId).Distinct().ToList();

                var dutyCompletionResults = await _dbContext.DutyCompletionResults
                    .Where(dcr => topXIds.Contains(dcr.Id))
                    .ToListAsync();

                var dutyMembers = await _dbContext.DutyMembers
                    .Where(dm => topXIds.Contains(dm.UploadId))
                    .ToListAsync();

                var users = await _dbContext.Users
                    .Where(u => userIds.Contains(u.UserId))
                    .ToListAsync();

                var territory = await _dbContext.Territories
                    .FirstAsync(t => t.TerritoryId == territoryId);

                var territoryDto = new TerritoryDto
                {
                    TerritoryId = territory.TerritoryId,
                    PlaceName = territory.PlaceName,
                    ContentName = territory.ContentName,
                    Level = territory.Level,
                    ContentType = territory.ContentType,
                    Expac = territory.Expac
                };
                                
                var resultDtos = topXResults.Select(result => new TopXCompletionResultDto
                {
                    Rank = result.Rank,
                    Duration = result.Duration,
                    DutyCompletionResult = dutyCompletionResults
                        .Where(dcr => dcr.Id == result.Id)
                        .Select(dcr => new DutyCompletionResultDto
                        { 
                            Id = dcr.Id,
                            UserId = dcr.UserId,
                            TerritoryId = dcr.TerritoryId,
                            HasEcho = dcr.HasEcho,
                            IsUnrestricted = dcr.IsUnrestricted,
                            IsMinILevel = dcr.IsMinILevel,
                            HasNpcMembers = dcr.HasNpcMembers,
                            StartTime = dcr.StartTime,
                            EndTime = dcr.EndTime,
                            Hours = dcr.Hours,
                            Minutes = dcr.Minutes,
                            Seconds = dcr.Seconds,
                            Milliseconds = dcr.Milliseconds,
                            GameVersion = dcr.GameVersion,
                            PluginVersion = dcr.PluginVersion,
                            Lang = dcr.Lang,
                            DataCenter = dcr.DataCenter,
                            UploadedAt = dcr.UploadedAt
                        }).First(),
                    DutyMembers = dutyMembers
                        .Where(dm => dm.UploadId == result.Id)
                        .Select(dm => new DutyMemberDto
                        {
                            UploadId = dm.UploadId,
                            GroupNumber = dm.GroupNumber,
                            MemberNumber = dm.MemberNumber,
                            Level = dm.Level,
                            ClassJob = dm.ClassJob,
                            IsNpc = dm.IsNpc,
                            IsPlayer = dm.IsPlayer,
                        }).ToList(),
                    User = users
                        .Where(u => u.UserId == result.UserId)
                        .Select(u => new UserDto
                        {
                            UserId = u.UserId,
                            UserName = u.UserName,
                            WorldId = u.WorldId
                        }).FirstOrDefault(),
                    Territory = territoryDto
                }).ToList();


                return resultDtos;
            });

       return records;
    }

    public async ValueTask<List<ContentTypeRecord>> GetContentTypeRecords(string contentType, CancellationToken cancellationToken)
    {
        var cacheKey = $"xpd-th";
        var cacheSeconds = 600;
        var records = await _memcachedClient.GetValueOrCreateAsync(cacheKey, cacheSeconds,
            async () => await _dbContext.ContentTypeRecords.FromSqlInterpolated($"SELECT * FROM public.get_contenttyperesults({contentType})")
                .ToListAsync(cancellationToken));

        return records;
    }
}
