using Common.Constant;
using Common.Logic;
using Database.Database;
using log4net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using SicboSub.Api.DTO;
using SicboSub.Api.Singleton;

namespace SicboSub.Api.Business
{
    public class GameBusinessImpl : IGameBusiness
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(GameBusinessImpl));

        private readonly ModelContext dbContext;
        private readonly IConfiguration configuration;

        public GameBusinessImpl(ModelContext _dbContext, IConfiguration _configuration)
        {
            dbContext = _dbContext;
            configuration = _configuration;
        }

        public async Task<IActionResult> PlayHistory(HttpRequest httpRequest, PlayHistoryReq request)
        {
            var url = httpRequest.Path;
            var json = JsonConvert.SerializeObject(request);
            log.Debug("URL: " + url + " => Request: " + json);

            try
            {
                string lang = CommonLogic.GetLanguage(httpRequest, request.lang);
                int pageNumber = request.pageNumber < 0 ? 0 : request.pageNumber;
                int pageSize = request.pageSize <= 0 ? 20 : request.pageSize;

                string token = httpRequest.Headers["Authorization"].ToString();
                if (string.IsNullOrEmpty(token))
                {
                    return DotnetLib.Http.HttpResponse.BuildResponse(
                        log, url, json,
                        CommonErrorCode.UnauthorizedAccess,
                        ConfigManager.Instance.GetConfigWebValue("UNAUTHORIZED", lang),
                        new { }
                    );
                }
                token = token.Replace("Bearer ", "");

                if (string.IsNullOrEmpty(request.msisdn))
                {
                    return DotnetLib.Http.HttpResponse.BuildResponse(
                        log, url, json,
                        CommonErrorCode.InvalidParameter,
                        ConfigManager.Instance.GetConfigWebValue("INVALID_PARAM", lang),
                        new { }
                    );
                }

                // Query BettingHistory
                var query = dbContext.BettingHistories.AsNoTracking()
                    .Where(b => b.Msisdn == request.msisdn);
                
                if (!string.IsNullOrEmpty(request.fromDate))
                {
                    if (DateTime.TryParseExact(request.fromDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime from))
                    {
                        query = query.Where(b => b.BettingTime >= from);
                    }
                }

                if (!string.IsNullOrEmpty(request.toDate))
                {
                    if (DateTime.TryParseExact(request.toDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime to))
                    {
                        query = query.Where(b => b.BettingTime <= to);
                    }
                }
                
                int totalCount = await query.CountAsync();
                int totalPages = (int)Math.Ceiling((double)totalCount / pageSize);

                // Fetch bets
                var bets = await query
                    .OrderByDescending(b => b.BettingTime)
                    .Skip(pageNumber * pageSize)
                    .Take(pageSize)
                    .ToListAsync();
                
                // Get Room Ids from bets
                var roomIds = bets.Select(b => b.RoomId).Distinct().ToList();

                // Query RoomPrize for these rooms and user
                var prizes = await dbContext.RoomPrizes.AsNoTracking()
                    .Where(p => p.Msisdn == request.msisdn && roomIds.Contains(p.RoomId))
                    .ToListAsync();

                // Merge
                var history = bets.Select(b => {
                    // Match prize by RoomId and Type == BettingType
                    var prize = prizes.FirstOrDefault(p => p.RoomId == b.RoomId && p.Type == b.BettingType);
                    
                    return new PlayHistoryItem
                    {
                        id = b.Id,
                        roomId = b.RoomId,
                        bettingType = b.BettingType,
                        bettingValue = b.BettingValue,
                        bettingTime = b.BettingTime,
                        winCoin = prize?.WinCoin ?? 0,
                        backCoin = prize?.BackCoin ?? 0,
                        isWin = prize != null && prize.WinCoin > 0
                    };
                }).ToList();

                return DotnetLib.Http.HttpResponse.BuildResponse(
                    log, url, json,
                    CommonErrorCode.Success,
                    ConfigManager.Instance.GetConfigWebValue("LOAD_SUCCESS", lang),
                    new
                    {
                        history = history,
                        pagination = new
                        {
                            pageNumber,
                            pageSize,
                            totalCount,
                            totalPages
                        }
                    }
                );
            }
            catch (Exception ex)
            {
                log.Error("Exception: ", ex);
            }

            return DotnetLib.Http.HttpResponse.BuildResponse(
                log, url, json,
                CommonErrorCode.SystemError,
                ConfigManager.Instance.GetConfigWebValue("SYSTEM_FAILURE"),
                new { }
            );
        }
        public async Task<IActionResult> RankingHistory(HttpRequest httpRequest, RankingHistoryReq request)
        {
            var url = httpRequest.Path;
            var json = JsonConvert.SerializeObject(request);
            log.Debug("URL: " + url + " => Request: " + json);

            try
            {
                string lang = CommonLogic.GetLanguage(httpRequest, request.lang);
                int pageNumber = request.pageNumber < 0 ? 0 : request.pageNumber;
                int pageSize = request.pageSize <= 0 ? 20 : request.pageSize;

                string token = httpRequest.Headers["Authorization"].ToString();
                if (string.IsNullOrEmpty(token))
                {
                    return DotnetLib.Http.HttpResponse.BuildResponse(
                        log, url, json,
                        CommonErrorCode.UnauthorizedAccess,
                        ConfigManager.Instance.GetConfigWebValue("UNAUTHORIZED", lang),
                        new { }
                    );
                }
                token = token.Replace("Bearer ", "");


                // Query RankingRewardLog joined with RankingHistory
                var query = from reward in dbContext.RankingRewardLogs.AsNoTracking()
                            join ranking in dbContext.RankingHistories.AsNoTracking() on reward.RankingHistoryId equals ranking.Id
                            select new { reward, ranking };

                if (!string.IsNullOrEmpty(request.rankType))
                {
                    query = query.Where(x => x.ranking.RankType == request.rankType);
                }
                if (!string.IsNullOrEmpty(request.msisdn))
                {
                    query = query.Where(x => x.ranking.Msisdn == request.msisdn);
                }

                if (!string.IsNullOrEmpty(request.fromDate))
                {
                    if (DateTime.TryParseExact(request.fromDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime from))
                    {
                        query = query.Where(x => x.ranking.RankDate >= from);
                    }
                }

                if (!string.IsNullOrEmpty(request.toDate))
                {
                    if (DateTime.TryParseExact(request.toDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime to))
                    {
                        query = query.Where(x => x.ranking.RankDate <= to);
                    }
                }

                int totalCount = await query.CountAsync();
                int totalPages = (int)Math.Ceiling((double)totalCount / pageSize);

                var items = await query
                    .OrderByDescending(x => x.reward.RewardDate)
                    .Skip(pageNumber * pageSize)
                    .Take(pageSize)
                    .Select(x => new RankingHistoryItem
                    {
                        id = x.reward.Id,
                        msisdn = x.reward.Msisdn,
                        rankType = x.ranking.RankType ?? "",
                        rankDate = x.ranking.RankDate ?? DateTime.MinValue,
                        rankPosition = x.ranking.RankPosition ?? 0,
                        rewardValue = x.reward.RewardValue ?? 0,
                        rewardUnit = x.reward.RewardUnit ?? "",
                        rewardDate = x.reward.RewardDate ?? DateTime.MinValue,
                        totalBetValue = x.ranking.TotalBetValue ?? 0
                    })
                    .ToListAsync();

                return DotnetLib.Http.HttpResponse.BuildResponse(
                    log, url, json,
                    CommonErrorCode.Success,
                    ConfigManager.Instance.GetConfigWebValue("LOAD_SUCCESS", lang),
                    new
                    {
                        history = items,
                        pagination = new
                        {
                            pageNumber,
                            pageSize,
                            totalCount,
                            totalPages
                        }
                    }
                );
            }
            catch (Exception ex)
            {
                log.Error("Exception: ", ex);
            }

            return DotnetLib.Http.HttpResponse.BuildResponse(
                log, url, json,
                CommonErrorCode.SystemError,
                ConfigManager.Instance.GetConfigWebValue("SYSTEM_FAILURE"),
                new { }
            );
        }
        public async Task<IActionResult> PurchaseHistory(HttpRequest httpRequest, PurchaseHistoryReq request)
        {
            var url = httpRequest.Path;
            var json = JsonConvert.SerializeObject(request);
            log.Debug("URL: " + url + " => Request: " + json);

            try
            {
                string lang = CommonLogic.GetLanguage(httpRequest, request.lang);
                int pageNumber = request.pageNumber < 0 ? 0 : request.pageNumber;
                int pageSize = request.pageSize <= 0 ? 20 : request.pageSize;

                string token = httpRequest.Headers["Authorization"].ToString();
                if (string.IsNullOrEmpty(token))
                {
                    return DotnetLib.Http.HttpResponse.BuildResponse(
                        log, url, json,
                        CommonErrorCode.UnauthorizedAccess,
                        ConfigManager.Instance.GetConfigWebValue("UNAUTHORIZED", lang),
                        new { }
                    );
                }
                token = token.Replace("Bearer ", "");

                if (string.IsNullOrEmpty(request.msisdn))
                {
                    return DotnetLib.Http.HttpResponse.BuildResponse(
                        log, url, json,
                        CommonErrorCode.InvalidParameter,
                        ConfigManager.Instance.GetConfigWebValue("INVALID_PARAM", lang),
                        new { }
                    );
                }

                // Query REGINFO with LEFT JOIN to PACKG to get Fee
                var query = from r in dbContext.RegInfos.AsNoTracking()
                            join p in dbContext.Packgs.AsNoTracking() 
                                on r.ProductName equals p.ProductName into packageGroup
                            from pkg in packageGroup.DefaultIfEmpty()
                            where r.Msisdn == request.msisdn
                            select new { r, Fee = pkg.Fee }; 
                
                if (!string.IsNullOrEmpty(request.fromDate))
                {
                    if (DateTime.TryParseExact(request.fromDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime from))
                    {
                        query = query.Where(x => x.r.RegisterTime >= from);
                    }
                }

                if (!string.IsNullOrEmpty(request.toDate))
                {
                    if (DateTime.TryParseExact(request.toDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime to))
                    {
                        query = query.Where(x => x.r.RegisterTime <= to);
                    }
                }

                int totalCount = await query.CountAsync();
                int totalPages = (int)Math.Ceiling((double)totalCount / pageSize);

                var items = await query
                    .OrderByDescending(x => x.r.RegisterTime)
                    .Skip(pageNumber * pageSize)
                    .Take(pageSize)
                    .Select(x => new PurchaseHistoryItem
                    {
                        id = x.r.RegisterId,
                        productName = x.r.ProductName ?? "",
                        registerTime = x.r.RegisterTime ?? DateTime.MinValue,
                        expireTime = x.r.ExpireTime ?? DateTime.MinValue,
                        serviceId = x.r.ServiceId ?? 0,
                        channel = x.r.Channel ?? "",
                        status = x.r.Status,
                        renew = x.r.Renew ?? 0,
                        fee = (int)(x.Fee ?? 0) // Fee from Packg table
                    })
                    .ToListAsync();
                
                return DotnetLib.Http.HttpResponse.BuildResponse(
                    log, url, json,
                    CommonErrorCode.Success,
                    ConfigManager.Instance.GetConfigWebValue("LOAD_SUCCESS", lang),
                    new
                    {
                        history = items,
                        pagination = new
                        {
                            pageNumber,
                            pageSize,
                            totalCount,
                            totalPages
                        }
                    }
                );
            }
            catch (Exception ex)
            {
                log.Error("PurchaseHistory: Exception", ex);
            }

            return DotnetLib.Http.HttpResponse.BuildResponse(
                log, url, json,
                CommonErrorCode.SystemError,
                ConfigManager.Instance.GetConfigWebValue("SYSTEM_FAILURE"),
                new { }
            );
        }

        public async Task<IActionResult> RankingCoinHistory(HttpRequest httpRequest, RankingHistoryReq request)
        {
            var url = httpRequest.Path;
            var json = JsonConvert.SerializeObject(request);
            log.Debug("URL: " + url + " => Request: " + json);

            try
            {
                string lang = CommonLogic.GetLanguage(httpRequest, request.lang);
                int pageNumber = request.pageNumber < 0 ? 0 : request.pageNumber;
                int pageSize = request.pageSize <= 0 ? 20 : request.pageSize;

                string token = httpRequest.Headers["Authorization"].ToString();
                if (string.IsNullOrEmpty(token))
                {
                    return DotnetLib.Http.HttpResponse.BuildResponse(
                        log, url, json,
                        CommonErrorCode.UnauthorizedAccess,
                        ConfigManager.Instance.GetConfigWebValue("UNAUTHORIZED", lang),
                        new { }
                    );
                }
                token = token.Replace("Bearer ", "");

                // Standalone standing query from RankingHistory
                var standingQuery = dbContext.BettingHistories.AsNoTracking();

                if (!string.IsNullOrEmpty(request.fromDate))
                {
                    if (DateTime.TryParseExact(request.fromDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime from))
                    {
                        standingQuery = standingQuery.Where(x => x.BettingTime >= from);
                    }
                }

                if (!string.IsNullOrEmpty(request.toDate))
                {
                    if (DateTime.TryParseExact(request.toDate, "dd/MM/yyyy HH:mm:ss", null, System.Globalization.DateTimeStyles.None, out DateTime to))
                    {
                        standingQuery = standingQuery.Where(x => x.BettingTime <= to);
                    }
                }

                // Group by Msisdn to calculate real-time totals
                var groupedQuery = standingQuery
                    .GroupBy(x => x.Msisdn)
                    .Select(g => new {
                        Msisdn = g.Key,
                        TotalBetValue = g.Sum(x => x.BettingValue)
                    });

                // Sorting the entire aggregated set to accurately position users
                var rankedList = await groupedQuery
                    .OrderByDescending(x => x.TotalBetValue)
                    .ToListAsync();

                var itemsStanding = rankedList
                    .Select((x, index) => new RankingHistoryItem
                    {
                        id = index + 1,
                        msisdn = x.Msisdn ?? "",
                        rankType = request.rankType ?? "",
                        rankDate = DateTime.Now,
                        rankPosition = index + 1,
                        rewardValue = 0,
                        rewardUnit = "",
                        rewardDate = DateTime.MinValue,
                        totalBetValue = x.TotalBetValue
                    })
                    .ToList();

                // Apply msisdn filter if requested for current user standings display
                if (!string.IsNullOrEmpty(request.msisdn))
                {
                    itemsStanding = itemsStanding.Where(x => x.msisdn == request.msisdn).ToList();
                }

                int totalCountStanding = itemsStanding.Count;
                int totalPagesStanding = (int)Math.Ceiling((double)totalCountStanding / pageSize);

                var paginationItems = itemsStanding
                    .Skip(pageNumber * pageSize)
                    .Take(pageSize)
                    .ToList();

                return DotnetLib.Http.HttpResponse.BuildResponse(
                    log, url, json,
                    CommonErrorCode.Success,
                    ConfigManager.Instance.GetConfigWebValue("LOAD_SUCCESS", lang),
                    new
                    {
                        history = paginationItems,
                        pagination = new
                        {
                            pageNumber,
                            pageSize,
                            totalCount = totalCountStanding,
                            totalPages = totalPagesStanding
                        }
                    }
                );
            }
            catch (Exception ex)
            {
                log.Error("Exception: ", ex);
            }

            return DotnetLib.Http.HttpResponse.BuildResponse(
                log, url, json,
                CommonErrorCode.SystemError,
                ConfigManager.Instance.GetConfigWebValue("SYSTEM_FAILURE"),
                new { }
            );
        }
    }
}
