using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.Common.Extensions;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.CheckIn;
using QYZH.InteractiveMagazine.Models.Dto.Compensation;
using QYZH.InteractiveMagazine.Models.Dto.Pet;
using QYZH.InteractiveMagazine.Models.Dto.Points;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;

namespace QYZH.InteractiveMagazine.Service;

/// <summary>
/// 签到服务实现
/// </summary>
public class CheckInService(
    BaseRepository<CheckInRecord> checkInRecordRepository,
    IPetService petService,
    ICompensationTaskService compensationTaskService,
    IPointsService pointsService,
    ILogger<CheckInService> logger)
    : BaseRepository<CheckInRecord>, ICheckInService
{
    /// <summary>
    /// 默认签到奖励积分（无配置时的兜底值）
    /// </summary>
    private const int DefaultRewardPoints = 10;

    /// <summary>
    /// 用户签到
    /// </summary>
    public async Task<CheckInOutput> CheckInAsync(long userId)
    {
        logger.LogInformation("用户签到，UserId: {UserId}", userId);

        var today = DateTime.Now.Date;

        // 1. 检查今日是否已签到
        var alreadyCheckedIn = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted && r.CheckInDate >= today && r.CheckInDate < today.AddDays(1))
            .AnyAsync();

        if (alreadyCheckedIn)
        {
            throw new BusinessException("今日已签到，请明天再来", ResultCode.BAD_REQUEST);
        }

        // 2. 计算连续签到天数
        var consecutiveDays = await CalculateConsecutiveDaysAsync(userId, today);

        // 3. 查询签到配置，计算奖励
        var (pointsReward, growthReward) = await CalculateRewardsAsync(consecutiveDays);

        // 4. 查询用户信息
        var user = await checkInRecordRepository.Context.Queryable<Users>()
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstAsync();

        if (user == null)
        {
            throw new BusinessException("用户不存在", ResultCode.NOT_FOUND);
        }

        // 5. 查询用户宠物（如果有）
        var pet = await checkInRecordRepository.Context.Queryable<UserPet>()
            .Where(p => p.UserId == userId && !p.IsDeleted)
            .FirstAsync();

        // 6. 事务执行签到相关写操作（含宠物喂养，统一事务）
        var result = new CheckInOutput();

        await checkInRecordRepository.UseTranAsync(async () =>
        {
            // 6a. 创建签到记录
            var checkInRecord = new CheckInRecord
            {
                UserId = userId,
                CheckInDate = today,
                PointsAwarded = pointsReward,
                GrowthPointsAwarded = growthReward,
                ConsecutiveDays = consecutiveDays,
                Type = CheckInRecordTypeEnum.Normal,
                Status = (int)CheckInRecordStatusEnum.Success,
                CreatedBy = userId.ToString(),
                UpdatedBy = userId.ToString()
            };
            var recordId = await checkInRecordRepository.Insertable(checkInRecord).ExecuteReturnIdentityAsync();
            checkInRecord.Id = recordId;

            // 6b. 更新用户成长值
            var newGrowthBalance = user.GrowthPoints + growthReward;

            await checkInRecordRepository.Context.Updateable<Users>()
                .SetColumns(u => u.GrowthPoints == newGrowthBalance)
                .Where(u => u.Id == userId && !u.IsDeleted)
                .ExecuteCommandAsync();

            // 6c. 通过积分服务增加积分
            var pointsResult = await pointsService.AddPointsInTranAsync(new AddPointsInput
            {
                UserId = userId,
                Amount = pointsReward,
                ChangeType = PointsChangeTypeEnum.SignIn,
                RelatedId = recordId,
                Description = $"签到奖励（连续{consecutiveDays}天）"
            });

            // 6d. 如果有活跃宠物，喂养宠物（同一事务内）
            FeedPetOutput? feedResult = null;
            if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
            {
                feedResult = await petService.FeedPetInTranAsync(userId, new FeedPetInput
                {
                    PetId = pet.Id,
                    GrowthPoints = growthReward
                });
            }

            // 构建返回结果
            result.RecordId = (long)recordId;
            result.CheckInDate = today;
            result.ConsecutiveDays = consecutiveDays;
            result.PointsAwarded = pointsReward;
            result.GrowthPointsAwarded = growthReward;
            result.PointsBalance = pointsResult.NewBalance;
            result.GrowthPointsBalance = newGrowthBalance;
            result.HasPet = pet != null;
            if (feedResult != null)
            {
                result.HasEvolved = feedResult.HasEvolved;
                result.EvolvedStageName = feedResult.EvolvedStageName;
            }
        });

        if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
        {
            logger.LogInformation("签到成长值已喂养宠物，PetId: {PetId}, 进化: {HasEvolved}",
                pet.Id, result.HasEvolved);
        }

        logger.LogInformation("用户签到成功，UserId: {UserId}, 连续{Days}天, 积分+{Points}, 成长值+{Growth}",
            userId, consecutiveDays, pointsReward, growthReward);

        return result;
    }

    /// <summary>
    /// 获取用户签到信息
    /// </summary>
    public async Task<CheckInInfoOutput> GetCheckInInfoAsync(long userId)
    {
        logger.LogInformation("获取签到信息，UserId: {UserId}", userId);

        var today = DateTime.Now.Date;

        // 今日是否已签到
        var hasCheckedInToday = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted && r.CheckInDate >= today && r.CheckInDate < today.AddDays(1))
            .AnyAsync();

        // 累计签到天数
        var totalCheckInDays = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted)
            .CountAsync();

        // 最近一次签到记录（用于获取连续天数）
        var lastRecord = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted)
            .OrderBy(r => r.CheckInDate, SqlSugar.OrderByType.Desc)
            .FirstAsync();

        // 判断连续天数：如果最后一次签到是今天或昨天，则连续天数延续
        var consecutiveDays = 0;
        if (lastRecord != null)
        {
            var lastDate = lastRecord.CheckInDate.Date;
            if (lastDate == today || lastDate == today.AddDays(-1))
            {
                consecutiveDays = lastRecord.ConsecutiveDays;
            }
        }

        // 查询用户余额
        var user = await checkInRecordRepository.Context.Queryable<Users>()
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstAsync();

        // 最近 7 天签到记录
        var thirtyDaysAgo = today.AddDays(-6);
        var recentRecords = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted && r.CheckInDate >= thirtyDaysAgo)
            .OrderBy(r => r.CheckInDate, SqlSugar.OrderByType.Desc)
            .Select(r => new CheckInRecordOutput
            {
                Id = (long)r.Id,
                CheckInDate = r.CheckInDate,
                CreatedAt = r.CreatedAt,
                ConsecutiveDays = r.ConsecutiveDays,
                PointsAwarded = r.PointsAwarded,
                GrowthPointsAwarded = r.GrowthPointsAwarded,
                Type = r.Type.ToString(),
                Status = r.Status.ToString()
            })
            .ToListAsync();

        return new CheckInInfoOutput
        {
            HasCheckedInToday = hasCheckedInToday,
            ConsecutiveDays = consecutiveDays,
            TotalCheckInDays = totalCheckInDays,
            PointsBalance = user?.Points ?? 0,
            GrowthPointsBalance = user?.GrowthPoints ?? 0,
            RecentRecords = recentRecords
        };
    }

    /// <summary>
    /// 补签（消耗补签卡，补签历史漏签日期）
    /// </summary>
    public async Task<CheckInOutput> MakeUpCheckInAsync(long userId, DateTime targetDate)
    {
        logger.LogInformation("用户补签，UserId: {UserId}, TargetDate: {Date}", userId, targetDate);

        targetDate = targetDate.Date;

        if (targetDate >= DateTime.Now.Date)
            throw new BusinessException("只能补签过去的日期", ResultCode.BAD_REQUEST);

        // 检查目标日期是否已有签到记录
        var alreadyCheckedIn = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted
                        && r.CheckInDate >= targetDate && r.CheckInDate < targetDate.AddDays(1))
            .AnyAsync();

        if (alreadyCheckedIn)
            throw new BusinessException($"{targetDate:yyyy-MM-dd} 已签到，无需补签", ResultCode.BAD_REQUEST);

        // 检查用户背包中是否有补签卡
        var makeUpCard = await checkInRecordRepository.Context.Queryable<UserBag>()
            .Where(b => b.UserId == userId
                        && b.ItemType == "MakeUpCard"
                        && b.Quantity > 0
                        && b.Status == (int)UserBagStatusEnum.Available
                        && !b.IsDeleted)
            .OrderByDescending(b => b.CreatedAt)
            .FirstAsync();

        if (makeUpCard == null)
            throw new BusinessException("补签卡不足，无法补签", ResultCode.BAD_REQUEST);

        // 查询用户信息
        var user = await checkInRecordRepository.Context.Queryable<Users>()
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstAsync();

        if (user == null)
            throw new BusinessException("用户不存在", ResultCode.NOT_FOUND);

        // 查询宠物
        var pet = await checkInRecordRepository.Context.Queryable<UserPet>()
            .Where(p => p.UserId == userId && !p.IsDeleted)
            .FirstAsync();

        // 补签奖励按基础值计算（不享受连续签到加成）
        var (pointsReward, growthReward) = await CalculateRewardsAsync(1);

        var result = new CheckInOutput();

        await checkInRecordRepository.UseTranAsync(async () =>
        {
            // 创建补签记录
            var checkInRecord = new CheckInRecord
            {
                UserId = userId,
                CheckInDate = targetDate,
                PointsAwarded = pointsReward,
                GrowthPointsAwarded = growthReward,
                ConsecutiveDays = 0,  // 补签不纳入连续天数
                Type = CheckInRecordTypeEnum.MakeUp,
                Status = (int)CheckInRecordStatusEnum.Success,
                IsDeleted = false,
                CreatedBy = userId.ToString(),
                CreatedAt = DateTime.Now,
                UpdatedBy = userId.ToString(),
                UpdatedAt = DateTime.Now
            };
            var recordId = await checkInRecordRepository.Insertable(checkInRecord).ExecuteReturnIdentityAsync();
            checkInRecord.Id = recordId;

            // 更新用户成长值
            var newGrowthBalance = user.GrowthPoints + growthReward;

            await checkInRecordRepository.Context.Updateable<Users>()
                .SetColumns(u => u.GrowthPoints == newGrowthBalance)
                .Where(u => u.Id == userId && !u.IsDeleted)
                .ExecuteCommandAsync();

            // 通过积分服务增加积分
            var pointsResult = await pointsService.AddPointsInTranAsync(new AddPointsInput
            {
                UserId = userId,
                Amount = pointsReward,
                ChangeType = PointsChangeTypeEnum.MakeUpSign,
                RelatedId = recordId,
                Description = $"补签奖励（{targetDate:yyyy-MM-dd}）"
            });

            // 扣减补签卡
            if (makeUpCard.Quantity <= 1)
            {
                await checkInRecordRepository.Context.Updateable<UserBag>()
                    .SetColumns(b => b.Status == (int)UserBagStatusEnum.Expired)
                    .SetColumns(b => b.Quantity == 0)
                    .SetColumns(b => b.UpdatedAt == DateTime.Now)
                    .Where(b => b.Id == makeUpCard.Id)
                    .ExecuteCommandAsync();
            }
            else
            {
                await checkInRecordRepository.Context.Updateable<UserBag>()
                    .SetColumns(b => b.Quantity == makeUpCard.Quantity - 1)
                    .SetColumns(b => b.UpdatedAt == DateTime.Now)
                    .Where(b => b.Id == makeUpCard.Id)
                    .ExecuteCommandAsync();
            }

            // 如果有活跃宠物，喂养宠物（同一事务内）
            FeedPetOutput? feedResult = null;
            if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
            {
                feedResult = await petService.FeedPetInTranAsync(userId, new Models.Dto.Pet.FeedPetInput
                {
                    PetId = pet.Id,
                    GrowthPoints = growthReward
                });
            }

            result.RecordId = (long)recordId;
            result.CheckInDate = targetDate;
            result.ConsecutiveDays = 0;
            result.PointsAwarded = pointsReward;
            result.GrowthPointsAwarded = growthReward;
            result.PointsBalance = pointsResult.NewBalance;
            result.GrowthPointsBalance = newGrowthBalance;
            result.HasPet = pet != null;
            if (feedResult != null)
            {
                result.HasEvolved = feedResult.HasEvolved;
                result.EvolvedStageName = feedResult.EvolvedStageName;
            }
        });

        if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
        {
            logger.LogInformation("补签成长值已喂养宠物，PetId: {PetId}, 进化: {HasEvolved}",
                pet.Id, result.HasEvolved);
        }

        logger.LogInformation("补签成功，UserId: {UserId}, Date: {Date}, 积分+{Points}, 成长值+{Growth}",
            userId, targetDate, pointsReward, growthReward);

        return result;
    }

    /// <summary>
    /// 获取用户漏签日期列表
    /// </summary>
    public async Task<List<DateTime>> GetMissedDatesAsync(long userId, int days = 7)
    {
        var startDate = DateTime.Now.Date.AddDays(-days);

        // 查询该时间段内所有签到记录
        var checkedDates = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted && r.CheckInDate >= startDate)
            .Select(r => r.CheckInDate.Date)
            .ToListAsync();

        var checkedDateSet = new HashSet<DateTime>(checkedDates);
        var missedDates = new List<DateTime>();

        // 遍历每一天，找出漏签的日期（排除今天，今天不算漏签）
        for (var date = startDate; date < DateTime.Now.Date; date = date.AddDays(1))
        {
            if (!checkedDateSet.Contains(date))
            {
                missedDates.Add(date);
            }
        }

        return missedDates;
    }

    /// <summary>
    /// 计算连续签到天数
    /// </summary>
    private async Task<int> CalculateConsecutiveDaysAsync(long userId, DateTime today)
    {
        var yesterday = today.AddDays(-1);

        var yesterdayRecord = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted && r.CheckInDate >= yesterday && r.CheckInDate < today)
            .FirstAsync();

        // 昨天有签到记录，连续天数 +1；否则从 1 开始
        return yesterdayRecord != null ? yesterdayRecord.ConsecutiveDays + 1 : 1;
    }

    /// <summary>
    /// 根据连续签到天数计算奖励（积分 + 成长值）
    /// </summary>
    private async Task<(int PointsReward, int GrowthReward)> CalculateRewardsAsync(int consecutiveDays)
    {
        // 查询签到配置（按 DayNumber 升序）
        var configs = await checkInRecordRepository.Context.Queryable<CheckInConfig>()
            .Where(c => c.Status == (int)DefaultStatusEnum.Active && !c.IsDeleted)
            .OrderBy(c => c.DayNumber)
            .ToListAsync();

        if (configs.Count == 0)
        {
            // 无配置时使用默认值
            return (DefaultRewardPoints, DefaultRewardPoints);
        }

        // 找到匹配的奖励档位：取 DayNumber <= 连续天数 的最大档位
        var matchedConfig = configs.LastOrDefault(c => c.DayNumber <= consecutiveDays)
            ?? configs.First();

        var totalPoints = matchedConfig.RewardPoints + matchedConfig.BonusPoints;

        // 成长值与积分相同（签到同时获得积分和成长值）
        return (totalPoints, totalPoints);
    }
}
