using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
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 PointsService(
    BaseRepository<PointsRecord> pointsRecordRepository,
    ILogger<PointsService> logger)
    : BaseRepository<PointsRecord>, IPointsService
{
    #region 带事务版本（独立调用）

    /// <summary>
    /// 增加积分（带事务）
    /// </summary>
    public async Task<AddPointsOutput> AddPointsAsync(AddPointsInput input)
    {
        logger.LogInformation("用户增加积分，UserId: {UserId}, Amount: {Amount}, Type: {Type}",
            input.UserId, input.Amount, input.ChangeType);

        if (input.Amount <= 0)
            throw new BusinessException("增加积分数量必须大于0", ResultCode.BAD_REQUEST);

        AddPointsOutput result = null!;

        await UseTranAsync(async () =>
        {
            result = await AddPointsInTranAsync(input);
        });

        return result;
    }

    /// <summary>
    /// 扣除积分（带事务）
    /// </summary>
    public async Task<DeductPointsOutput> DeductPointsAsync(DeductPointsInput input)
    {
        logger.LogInformation("用户扣除积分，UserId: {UserId}, Amount: {Amount}, Type: {Type}",
            input.UserId, input.Amount, input.ChangeType);

        if (input.Amount <= 0)
            throw new BusinessException("扣除积分数量必须大于0", ResultCode.BAD_REQUEST);

        DeductPointsOutput result = null!;

        await UseTranAsync(async () =>
        {
            result = await DeductPointsInTranAsync(input);
        });

        return result;
    }

    #endregion

    #region 无事务版本（供外部事务调用）

    /// <summary>
    /// 增加积分（无事务，需在外部事务中调用）
    /// </summary>
    public async Task<AddPointsOutput> AddPointsInTranAsync(AddPointsInput input)
    {
        if (input.Amount <= 0)
            throw new BusinessException("增加积分数量必须大于0", ResultCode.BAD_REQUEST);

        // 查询用户当前积分
        var user = await Context.Queryable<Users>()
            .Where(u => u.Id == input.UserId && !u.IsDeleted)
            .FirstAsync();

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

        var previousBalance = user.Points;
        var newBalance = previousBalance + input.Amount;

        // 更新用户积分
        await Context.Updateable<Users>()
            .SetColumns(u => u.Points == newBalance)
            .SetColumns(u => u.UpdatedAt == DateTime.Now)
            .Where(u => u.Id == input.UserId && !u.IsDeleted)
            .ExecuteCommandAsync();

        // 插入积分流水记录
        var record = new PointsRecord
        {
            UserId = input.UserId,
            ChangeAmount = input.Amount,
            BalanceAfter = newBalance,
            ChangeType = input.ChangeType.ToString(),
            RelatedId = input.RelatedId,
            Description = input.Description,
            Type = PointsFlowTypeEnum.Income,
            Status = (int)PointsRecordStatusEnum.Success,
            IsDeleted = false,
            CreatedBy = input.OperatorName ?? user.Name ?? input.UserId.ToString(),
            CreatedAt = DateTime.Now,
            UpdatedBy = input.OperatorName ?? user.Name ?? input.UserId.ToString(),
            UpdatedAt = DateTime.Now
        };

        var recordEntity = await InsertReturnEntityAsync(record);

        logger.LogInformation("增加积分成功，UserId: {UserId}, 积分: {Before} -> {After}, 变动: +{Amount}",
            input.UserId, previousBalance, newBalance, input.Amount);

        return new AddPointsOutput
        {
            RecordId = recordEntity.Id,
            PreviousBalance = previousBalance,
            NewBalance = newBalance,
            AddedAmount = input.Amount
        };
    }

    /// <summary>
    /// 扣除积分（无事务，需在外部事务中调用）
    /// </summary>
    public async Task<DeductPointsOutput> DeductPointsInTranAsync(DeductPointsInput input)
    {
        if (input.Amount <= 0)
            throw new BusinessException("扣除积分数量必须大于0", ResultCode.BAD_REQUEST);

        // 查询用户当前积分
        var user = await Context.Queryable<Users>()
            .Where(u => u.Id == input.UserId && !u.IsDeleted)
            .FirstAsync();

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

        var previousBalance = user.Points;

        // 余额不足校验
        if (previousBalance < input.Amount)
            throw new BusinessException($"积分不足，需要 {input.Amount} 积分，当前余额 {previousBalance}", ResultCode.BAD_REQUEST);

        var newBalance = previousBalance - input.Amount;

        // 更新用户积分
        await Context.Updateable<Users>()
            .SetColumns(u => u.Points == newBalance)
            .SetColumns(u => u.UpdatedAt == DateTime.Now)
            .Where(u => u.Id == input.UserId && !u.IsDeleted)
            .ExecuteCommandAsync();

        // 插入积分流水记录
        var record = new PointsRecord
        {
            UserId = input.UserId,
            ChangeAmount = -input.Amount,
            BalanceAfter = newBalance,
            ChangeType = input.ChangeType.ToString(),
            RelatedId = input.RelatedId,
            Description = input.Description,
            Type = PointsFlowTypeEnum.Expense,
            Status = (int)PointsRecordStatusEnum.Success,
            IsDeleted = false,
            CreatedBy = input.OperatorName ?? user.Name ?? input.UserId.ToString(),
            CreatedAt = DateTime.Now,
            UpdatedBy = input.OperatorName ?? user.Name ?? input.UserId.ToString(),
            UpdatedAt = DateTime.Now
        };

        var recordEntity = await InsertReturnEntityAsync(record);

        logger.LogInformation("扣除积分成功，UserId: {UserId}, 积分: {Before} -> {After}, 变动: -{Amount}",
            input.UserId, previousBalance, newBalance, input.Amount);

        return new DeductPointsOutput
        {
            RecordId = recordEntity.Id,
            PreviousBalance = previousBalance,
            NewBalance = newBalance,
            DeductedAmount = input.Amount
        };
    }

    #endregion

    #region 查询

    /// <summary>
    /// 查询用户当前积分余额
    /// </summary>
    public async Task<int> GetUserPointsAsync(long userId)
    {
        var user = await Context.Queryable<Users>()
            .Where(u => u.Id == userId && !u.IsDeleted)
            .Select(u => u.Points)
            .FirstAsync();

        return user;
    }

    /// <summary>
    /// 获取用户积分概览
    /// </summary>
    public async Task<PointsSummaryOutput> GetPointsSummaryAsync(long userId)
    {
        // 查询用户
        var user = await Context.Queryable<Users>()
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstAsync();

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

        // 查询累计收入（Income 类型）
        var totalIncome = await Context.Queryable<PointsRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted && r.Type == PointsFlowTypeEnum.Income && r.Status == (int)PointsRecordStatusEnum.Success)
            .SumAsync(r => r.ChangeAmount);

        // 查询累计支出（Expense 类型，取绝对值）
        var totalExpense = await Context.Queryable<PointsRecord>()
            .Where(r => r.UserId == userId && !r.IsDeleted && r.Type == PointsFlowTypeEnum.Expense && r.Status == (int)PointsRecordStatusEnum.Success)
            .SumAsync(r => r.ChangeAmount);

        return new PointsSummaryOutput
        {
            UserId = userId,
            CurrentBalance = user.Points,
            TotalIncome = totalIncome,
            TotalExpense = Math.Abs(totalExpense)
        };
    }

    /// <summary>
    /// 分页查询积分流水
    /// </summary>
    public async Task<PageListModel<PointsRecordOutput>> GetPointsRecordsAsync(PointsRecordQueryInput input)
    {
        if (input.PageIndex <= 0)
            throw new BusinessException("页码必须大于0", ResultCode.BAD_REQUEST);

        if (input.PageSize <= 0 || input.PageSize > 100)
            throw new BusinessException("每页条数必须在1-100之间", ResultCode.BAD_REQUEST);

        var query = Context.Queryable<PointsRecord>()
            .Where(r => r.UserId == input.UserId && !r.IsDeleted)
            .WhereIF(input.ChangeType.HasValue, r => r.ChangeType == input.ChangeType.Value.ToString())
            .WhereIF(input.Type.HasValue, r => r.Type == input.Type.Value)
            .WhereIF(!string.IsNullOrEmpty(input.Status), r => r.Status.ToString() == input.Status)
            .OrderByDescending(r => r.CreatedAt);

        var total = 0;
        var records = await query
            .Select(r => new PointsRecordOutput
            {
                Id = (long)r.Id,
                ChangeAmount = r.ChangeAmount,
                BalanceAfter = r.BalanceAfter,
                ChangeType = r.ChangeType,
                Description = r.Description,
                Type = r.Type.ToString(),
                CreatedAt = r.CreatedAt
            })
            .ToPageListAsync(input.PageIndex, input.PageSize, total);

        return new PageListModel<PointsRecordOutput>(records, input.PageIndex, input.PageSize, total);
    }

    #endregion
}
