using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Sign;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace HoneyBox.Core.Services;

/// <summary>
/// 签到服务实现
/// </summary>
public class SignService : ISignService
{
    private readonly HoneyBoxDbContext _dbContext;
    private readonly IRedisService _redisService;
    private readonly ILogger<SignService> _logger;

    // 奖励类型映射
    private static readonly Dictionary<int, string> RewardTypeTexts = new()
    {
        { 1, "钻石" },
        { 2, "HH币" },
        { 3, "哈尼券" },
        { 4, "优惠券" }
    };

    public SignService(
        HoneyBoxDbContext dbContext,
        IRedisService redisService,
        ILogger<SignService> logger)
    {
        _dbContext = dbContext;
        _redisService = redisService;
        _logger = logger;
    }

    /// <inheritdoc />
    public async Task<SignInfoResponse> GetSignInfoAsync(int userId)
    {
        var now = DateTime.Now;
        var currentMonth = now.Month;
        var currentYear = now.Year;
        var currentDate = now.ToString("yyyy-MM-dd");
        var totalDays = DateTime.DaysInMonth(currentYear, currentMonth);
        var nowDay = now.Day;
        var restDays = totalDays - nowDay;

        // 获取用户签到信息
        var (days, isSign) = await GetUserSignInfoAsync(userId, currentMonth, currentYear, currentDate);

        // 获取本月已签到天数
        var signDays = await GetMonthSignDaysAsync(userId, currentMonth, currentYear);

        // 查询本月累计签到领取记录
        var leijiList = await _dbContext.UserSigns
            .Where(s => s.UserId == userId && s.Month == currentMonth && s.Year == currentYear && s.SignType == 1)
            .Select(s => s.Days)
            .ToListAsync();

        // 获取每日签到配置 (type=1)
        var dailyConfigs = await _dbContext.SignConfigs
            .Where(c => c.Type == 1 && c.Status == 1 && c.Day <= totalDays)
            .OrderBy(c => c.Sort)
            .ToListAsync();

        // 获取累计签到配置 (type=2)
        var continuousConfigs = await _dbContext.SignConfigs
            .Where(c => c.Type == 2 && c.Status == 1)
            .OrderBy(c => c.Sort)
            .ToListAsync();

        // 计算下一个签到日
        var signDaysForNext = isSign == 0 ? signDays + 1 : signDays;

        // 处理每日签到配置
        var dailyConfigDtos = await ProcessDailyConfigsAsync(dailyConfigs, signDays, signDaysForNext);

        // 处理累计签到配置
        var continuousConfigDtos = await ProcessContinuousConfigsAsync(continuousConfigs, signDays, leijiList);

        return new SignInfoResponse
        {
            Days = days,
            IsSign = isSign,
            SignDays = signDays,
            TotalDays = totalDays,
            RestDays = restDays,
            DailyConfigs = dailyConfigDtos,
            ContinuousConfigs = continuousConfigDtos
        };
    }

    /// <inheritdoc />
    public async Task<SignInfoResponse> GetSignConfigAsync()
    {
        var now = DateTime.Now;
        var currentMonth = now.Month;
        var currentYear = now.Year;
        var totalDays = DateTime.DaysInMonth(currentYear, currentMonth);
        var nowDay = now.Day;
        var restDays = totalDays - nowDay;

        // 获取每日签到配置 (type=1)
        var dailyConfigs = await _dbContext.SignConfigs
            .Where(c => c.Type == 1 && c.Status == 1 && c.Day <= totalDays)
            .OrderBy(c => c.Sort)
            .ToListAsync();

        // 获取累计签到配置 (type=2)
        var continuousConfigs = await _dbContext.SignConfigs
            .Where(c => c.Type == 2 && c.Status == 1)
            .OrderBy(c => c.Sort)
            .ToListAsync();

        // 处理每日签到配置（未登录状态，所有都显示未签到）
        var dailyConfigDtos = await ProcessDailyConfigsAsync(dailyConfigs, 0, 1);

        // 处理累计签到配置（未登录状态，所有都显示未达到）
        var continuousConfigDtos = await ProcessContinuousConfigsAsync(continuousConfigs, 0, new List<int>());

        return new SignInfoResponse
        {
            Days = 0,
            IsSign = 0,
            SignDays = 0,
            TotalDays = totalDays,
            RestDays = restDays,
            DailyConfigs = dailyConfigDtos,
            ContinuousConfigs = continuousConfigDtos
        };
    }


    /// <inheritdoc />
    public async Task<SignResultResponse> DoSignAsync(int userId)
    {
        var lockKey = $"user_sign_lock:{userId}";
        var lockValue = Guid.NewGuid().ToString();

        // 尝试获取分布式锁
        var lockAcquired = await _redisService.TryAcquireLockAsync(lockKey, lockValue, TimeSpan.FromSeconds(3));
        if (!lockAcquired)
        {
            throw new InvalidOperationException("操作频繁，请稍后再试");
        }

        try
        {
            var now = DateTime.Now;
            var signDate = now.ToString("yyyy-MM-dd");
            var currentMonth = now.Month;
            var currentYear = now.Year;

            // 检查今日是否已签到
            var todaySign = await _dbContext.UserSigns
                .FirstOrDefaultAsync(s => s.UserId == userId && s.SignDate == signDate);

            if (todaySign != null)
            {
                throw new InvalidOperationException("今日您已签到");
            }

            // 计算连续签到天数
            var days = await CalculateContinuousDaysAsync(userId, signDate, currentMonth, currentYear);

            // 创建签到记录
            var userSign = new UserSign
            {
                UserId = userId,
                SignDate = signDate,
                SignDay = (byte)now.Day,
                Days = days,
                Month = currentMonth,
                Year = currentYear,
                Num = 1,
                SignType = 0,
                CreatedAt = now,
                UpdatedAt = now
            };

            _dbContext.UserSigns.Add(userSign);
            await _dbContext.SaveChangesAsync();

            // 获取本月签到天数
            var dayCount = await GetMonthSignDaysAsync(userId, currentMonth, currentYear);

            // 处理签到奖励
            var rewardMessages = new List<string>();

            // 处理每日签到奖励 (type=1)
            var dailyRewardMsg = await ProcessSignRewardsAsync(userId, 1, dayCount, "每日签到获得");
            if (!string.IsNullOrEmpty(dailyRewardMsg))
            {
                rewardMessages.Add(dailyRewardMsg);
            }

            // 处理累计签到奖励 (type=2)
            var continuousRewardMsg = await ProcessSignRewardsAsync(userId, 2, dayCount, "累计签到获得");
            if (!string.IsNullOrEmpty(continuousRewardMsg))
            {
                rewardMessages.Add(continuousRewardMsg);
            }

            var rewardMsg = string.Join(",", rewardMessages);
            if (rewardMsg.EndsWith(","))
            {
                rewardMsg = rewardMsg.TrimEnd(',');
            }

            _logger.LogInformation("用户签到成功: UserId={UserId}, Days={Days}, SignDays={SignDays}", 
                userId, days, dayCount);

            return new SignResultResponse
            {
                SignDays = dayCount,
                RewardMsg = rewardMsg
            };
        }
        finally
        {
            // 释放分布式锁
            await _redisService.ReleaseLockAsync(lockKey, lockValue);
        }
    }

    #region Private Methods

    /// <summary>
    /// 获取用户签到信息
    /// </summary>
    private async Task<(int days, int isSign)> GetUserSignInfoAsync(int userId, int currentMonth, int currentYear, string currentDate)
    {
        var latestSign = await _dbContext.UserSigns
            .Where(s => s.UserId == userId && s.Month == currentMonth && s.Year == currentYear && s.SignType == 0)
            .OrderByDescending(s => s.CreatedAt)
            .FirstOrDefaultAsync();

        if (latestSign == null)
        {
            return (0, 0);
        }

        var days = latestSign.Days;
        var isSign = latestSign.SignDate == currentDate ? 1 : 0;

        return (days, isSign);
    }

    /// <summary>
    /// 获取本月签到天数
    /// </summary>
    private async Task<int> GetMonthSignDaysAsync(int userId, int currentMonth, int currentYear)
    {
        return await _dbContext.UserSigns
            .CountAsync(s => s.UserId == userId && s.Month == currentMonth && s.Year == currentYear && s.SignType == 0);
    }

    /// <summary>
    /// 计算连续签到天数
    /// </summary>
    private async Task<int> CalculateContinuousDaysAsync(int userId, string signDate, int currentMonth, int currentYear)
    {
        var latestSign = await _dbContext.UserSigns
            .Where(s => s.UserId == userId)
            .OrderByDescending(s => s.CreatedAt)
            .FirstOrDefaultAsync();

        if (latestSign == null)
        {
            return 1; // 第一次签到
        }

        // 如果是新的一个月，重置签到天数
        if (latestSign.Month != currentMonth || latestSign.Year != currentYear)
        {
            return 1;
        }

        // 判断是否今天已经签到
        if (latestSign.SignDate == signDate)
        {
            return -1; // 今日已签到
        }

        // 计算两次签到的时间差（天数）
        var lastSignDate = DateTime.Parse(latestSign.SignDate);
        var currentSignDate = DateTime.Parse(signDate);
        var daysDiff = (currentSignDate - lastSignDate).Days;

        if (daysDiff > 1)
        {
            return 1; // 不是连续签到，重置为1
        }

        return latestSign.Days + 1; // 连续签到
    }


    /// <summary>
    /// 处理每日签到配置
    /// </summary>
    private async Task<List<SignConfigDto>> ProcessDailyConfigsAsync(
        List<SignConfig> configs, int signDays, int signDaysForNext)
    {
        var result = new List<SignConfigDto>();

        foreach (var config in configs)
        {
            var day = config.Day ?? 0;
            var rewards = await GetRewardsAsync(config.RewardId);
            var description = BuildRewardDescription(rewards);

            var dto = new SignConfigDto
            {
                Id = config.Id,
                RewardId = config.RewardId,
                Day = day,
                Description = description,
                Sort = config.Sort,
                Icon = config.Icon,
                Title = $"第{day}天",
                Rewards = rewards,
                IsSign = signDays >= day ? 2 : 0,
                IsSignDay = signDaysForNext == day ? 1 : 0
            };

            result.Add(dto);
        }

        return result;
    }

    /// <summary>
    /// 处理累计签到配置
    /// </summary>
    private async Task<List<SignConfigDto>> ProcessContinuousConfigsAsync(
        List<SignConfig> configs, int signDays, List<int> leijiList)
    {
        var result = new List<SignConfigDto>();

        foreach (var config in configs)
        {
            var day = config.Day ?? 0;
            var rewards = await GetRewardsAsync(config.RewardId);
            var description = BuildRewardDescription(rewards);

            int isSign;
            if (leijiList.Contains(day))
            {
                isSign = 2; // 已领取
            }
            else if (signDays >= day)
            {
                isSign = 1; // 可领取
            }
            else
            {
                isSign = 0; // 未达到
            }

            var dto = new SignConfigDto
            {
                Id = config.Id,
                RewardId = config.RewardId,
                Day = day,
                Description = description,
                Sort = config.Sort,
                Icon = config.Icon,
                Title = $"累计签到{day}天",
                Rewards = rewards,
                IsSign = isSign,
                IsSignDay = 0
            };

            result.Add(dto);
        }

        return result;
    }

    /// <summary>
    /// 获取奖励列表
    /// </summary>
    private async Task<List<RewardDto>> GetRewardsAsync(string rewardId)
    {
        var rewards = await _dbContext.Rewards
            .Where(r => r.RewardId == rewardId)
            .Select(r => new RewardDto
            {
                RewardType = r.RewardType,
                RewardExtend = r.RewardExtend,
                RewardValue = r.RewardValue,
                RewardTypeText = GetRewardTypeText(r.RewardType)
            })
            .ToListAsync();

        return rewards;
    }

    /// <summary>
    /// 构建奖励描述
    /// </summary>
    private static string BuildRewardDescription(List<RewardDto> rewards)
    {
        if (rewards.Count == 0)
        {
            return string.Empty;
        }

        var descriptions = rewards.Select(r => 
            $"{RemoveTrailingZeros(r.RewardValue)}{r.RewardTypeText}");
        
        return string.Join(",", descriptions);
    }

    /// <summary>
    /// 移除小数点后的零
    /// </summary>
    private static string RemoveTrailingZeros(decimal value)
    {
        return value.ToString("0.##");
    }

    /// <summary>
    /// 获取奖励类型文本
    /// </summary>
    private static string GetRewardTypeText(int rewardType)
    {
        return RewardTypeTexts.TryGetValue(rewardType, out var text) ? text : "未知";
    }


    /// <summary>
    /// 处理签到奖励
    /// </summary>
    private async Task<string?> ProcessSignRewardsAsync(int userId, int type, int day, string prefix)
    {
        // 查找符合条件的配置
        var config = await _dbContext.SignConfigs
            .FirstOrDefaultAsync(c => c.Type == type && c.Status == 1 && c.Day == day);

        if (config == null)
        {
            return null;
        }

        // 获取奖励
        var rewards = await _dbContext.Rewards
            .Where(r => r.RewardId == config.RewardId)
            .ToListAsync();

        if (rewards.Count == 0)
        {
            return null;
        }

        var messages = new List<string>();

        foreach (var reward in rewards)
        {
            var rewardValue = reward.RewardValue;
            var rewardTypeText = GetRewardTypeText(reward.RewardType);

            // 发放奖励
            await SendRewardAsync(userId, reward.RewardType, rewardValue, reward.RewardExtend, 
                type == 1 ? "签到奖励" : "累计签到奖励");

            messages.Add($"{RemoveTrailingZeros(rewardValue)}{rewardTypeText}");
        }

        // 如果是累计签到奖励，记录领取记录
        if (type == 2)
        {
            var now = DateTime.Now;
            var leijiRecord = new UserSign
            {
                UserId = userId,
                SignDate = now.ToString("yyyy-MM-dd"),
                SignDay = (byte)now.Day,
                Days = day,
                Month = now.Month,
                Year = now.Year,
                Num = 1,
                SignType = 1, // 累计签到领取记录
                CreatedAt = now,
                UpdatedAt = now
            };
            _dbContext.UserSigns.Add(leijiRecord);
            await _dbContext.SaveChangesAsync();
        }

        return messages.Count > 0 ? $"{prefix}：{string.Join(",", messages)}" : null;
    }

    /// <summary>
    /// 发放奖励
    /// </summary>
    private async Task SendRewardAsync(int userId, int rewardType, decimal rewardValue, int? rewardExtend, string content)
    {
        var now = DateTime.Now;

        switch (rewardType)
        {
            case 1: // 钻石
                await AddUserDiamondAsync(userId, rewardValue, content);
                break;
            case 2: // UU币
                await AddUserUuCoinAsync(userId, rewardValue, content);
                break;
            case 3: // 达达卷
                await AddUserDadaCouponAsync(userId, rewardValue, content);
                break;
            case 4: // 优惠券
                if (rewardExtend.HasValue && rewardExtend.Value > 0)
                {
                    await AddUserCouponAsync(userId, rewardExtend.Value);
                }
                break;
        }
    }

    /// <summary>
    /// 增加用户钻石 (Money2)
    /// </summary>
    private async Task AddUserDiamondAsync(int userId, decimal amount, string content)
    {
        var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == userId);
        if (user == null) return;

        user.Money2 = (user.Money2 ?? 0) + amount;
        user.UpdatedAt = DateTime.Now;

        // 记录流水
        var profit = new ProfitMoney2
        {
            UserId = userId,
            Type = 1,
            ChangeMoney = amount,
            Money = user.Money2 ?? 0,
            Content = content,
            CreatedAt = DateTime.Now
        };
        _dbContext.ProfitMoney2s.Add(profit);
        await _dbContext.SaveChangesAsync();
    }

    /// <summary>
    /// 增加用户UU币
    /// </summary>
    private async Task AddUserUuCoinAsync(int userId, decimal amount, string content)
    {
        var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == userId);
        if (user == null) return;

        user.Integral = user.Integral + amount;
        user.UpdatedAt = DateTime.Now;

        // 记录流水
        var profit = new ProfitIntegral
        {
            UserId = userId,
            Type = 1,
            ChangeMoney = amount,
            Money = user.Integral,
            Content = content,
            CreatedAt = DateTime.Now
        };
        _dbContext.ProfitIntegrals.Add(profit);
        await _dbContext.SaveChangesAsync();
    }

    /// <summary>
    /// 增加用户达达卷
    /// </summary>
    private async Task AddUserDadaCouponAsync(int userId, decimal amount, string content)
    {
        var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == userId);
        if (user == null) return;

        user.Score = user.Score + amount;
        user.UpdatedAt = DateTime.Now;

        // 记录流水 - 达达卷使用 ProfitMoney 记录
        var profit = new ProfitMoney
        {
            UserId = userId,
            Type = 1,
            ChangeMoney = amount,
            Money = user.Score,
            Content = content,
            CreatedAt = DateTime.Now
        };
        _dbContext.ProfitMoneys.Add(profit);
        await _dbContext.SaveChangesAsync();
    }

    /// <summary>
    /// 增加用户优惠券
    /// </summary>
    private async Task AddUserCouponAsync(int userId, int couponId)
    {
        var coupon = await _dbContext.Coupons.FirstOrDefaultAsync(c => c.Id == couponId && c.Status == 1);
        if (coupon == null) return;

        var now = DateTime.Now;
        var effectiveDays = coupon.EffectiveDay ?? 30;
        var endTime = now.AddDays(effectiveDays);

        var userCoupon = new CouponReceife
        {
            UserId = userId,
            CouponId = couponId,
            Title = coupon.Title,
            Price = coupon.Price,
            ManPrice = coupon.ManPrice,
            Ttype = coupon.Ttype ?? 0,
            Status = 1,
            State = 0,
            EndTime = endTime,
            CreatedAt = now
        };
        _dbContext.CouponReceives.Add(userCoupon);
        await _dbContext.SaveChangesAsync();
    }

    #endregion
}
