using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Club;
using CMSMicroservice.Domain.Entities.Commission;
using CMSMicroservice.Domain.Entities.History;
using CMSMicroservice.Domain.Enums;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.Application.ClubMembershipCQ.Commands.AcceptClubMembershipContract;

/// <summary>
/// Handler برای پذیرش قرارداد باشگاه مشتریان
/// این handler:
/// 1. کد OTP را تایید می‌کند
/// 2. قرارداد را در جدول UserContract ثبت می‌کند
/// 3. باشگاه مشتری را فعال می‌کند (IsActive = true)
/// 4. مبلغ را به Pool هفته جاری اضافه می‌کند
/// 5. ویژگی‌های باشگاه را به کاربر اعطا می‌کند
/// </summary>
public class AcceptClubMembershipContractCommandHandler 
    : IRequestHandler<AcceptClubMembershipContractCommand, AcceptClubMembershipContractResponseDto>
{
    private readonly IApplicationDbContext _context;
    private readonly IConfiguration _cfg;
    private readonly IHashService _hashService;
    private readonly ICurrentUserService _currentUser;
    private readonly IWeekDefinitionRepository _weekRepository;
    private readonly ILogger<AcceptClubMembershipContractCommandHandler> _logger;
    
    private const int MaxAttempts = 5;
    private const string OtpPurpose = "signClubContract";

    public AcceptClubMembershipContractCommandHandler(
        IApplicationDbContext context,
        IConfiguration cfg,
        IHashService hashService,
        ICurrentUserService currentUser,
        IWeekDefinitionRepository weekRepository,
        ILogger<AcceptClubMembershipContractCommandHandler> logger)
    {
        _context = context;
        _cfg = cfg;
        _hashService = hashService;
        _currentUser = currentUser;
        _weekRepository = weekRepository;
        _logger = logger;
    }

    public async Task<AcceptClubMembershipContractResponseDto> Handle(
        AcceptClubMembershipContractCommand request,
        CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "Processing club membership contract for UserId: {UserId}",
            request.UserId
        );

        // 1. دریافت کاربر
        var user = await _context.Users
            .Include(u => u.ClubMembership)
            .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);

        if (user == null)
        {
            _logger.LogWarning("User not found: {UserId}", request.UserId);
            throw new NotFoundException(nameof(User), request.UserId);
        }

        // 2. بررسی خرید پکیج
        if (user.PackagePurchaseMethod == PackagePurchaseMethod.None)
        {
            return new AcceptClubMembershipContractResponseDto
            {
                Success = false,
                Message = "برای امضای قرارداد باشگاه مشتریان ابتدا باید پکیج پایه را خریداری کنید"
            };
        }

        // 3. بررسی عدم فعال بودن قبلی باشگاه
        if (user.ClubMembership?.IsActive == true)
        {
            return new AcceptClubMembershipContractResponseDto
            {
                Success = false,
                Message = "شما قبلاً عضو باشگاه مشتریان شده‌اید"
            };
        }

        // 4. تایید OTP
        var otpResult = await VerifyOtpAsync(user.Mobile, request.OtpCode, cancellationToken);
        if (!otpResult.Success)
        {
            return new AcceptClubMembershipContractResponseDto
            {
                Success = false,
                Message = otpResult.Message
            };
        }

        // 5. ثبت قرارداد در جدول UserContract
        var contract = await _context.Contracts
            .FirstOrDefaultAsync(c => c.Type == ContractType.ClubMembership, cancellationToken);

        if (contract == null)
        {
            // اگر قرارداد وجود ندارد، یک قرارداد پیش‌فرض ایجاد کنید
            contract = new Contract
            {
                Title = "قرارداد باشگاه مشتریان",
                Description = "قوانین و مقررات باشگاه مشتریان کارابازار",
                HtmlContent = request.ContractHtml,
                Type = ContractType.ClubMembership
            };
            await _context.Contracts.AddAsync(contract, cancellationToken);
            await _context.SaveChangesAsync(cancellationToken);
        }

        var userContract = new UserContract
        {
            UserId = user.Id,
            ContractId = contract.Id,
            SignGuid = request.SignGuid,
            SignedPdfFile = request.ContractHtml
        };
        await _context.UserContracts.AddAsync(userContract, cancellationToken);

        // 6. دریافت مقادیر از SystemConstants (استاتیک)
        long giftValue = SystemConstants.ClubMembershipGiftValue;
        long activationFeeValue = SystemConstants.ClubActivationFee;
        
        _logger.LogInformation(
            "Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}",
            giftValue, activationFeeValue
        );

        // 7. فعال‌سازی باشگاه مشتریان
        ClubMembership clubMembership;
        bool isNewMembership = user.ClubMembership == null;
        var activationDate = DateTime.Now;

        if (isNewMembership)
        {
            clubMembership = new ClubMembership
            {
                UserId = user.Id,
                IsActive = true,
                ActivatedAt = activationDate,
                InitialContribution = activationFeeValue,
                GiftValue = giftValue,
                TotalEarned = 0,
                PurchaseMethod = user.PackagePurchaseMethod
            };
            await _context.ClubMemberships.AddAsync(clubMembership, cancellationToken);
            
            _logger.LogInformation(
                "Created new club membership for UserId {UserId} via {Method}, GiftValue: {GiftValue}",
                user.Id,
                user.PackagePurchaseMethod,
                giftValue
            );
        }
        else
        {
            clubMembership = user.ClubMembership!;
            clubMembership.IsActive = true;
            clubMembership.ActivatedAt = activationDate;
            clubMembership.PurchaseMethod = user.PackagePurchaseMethod;
            _context.ClubMemberships.Update(clubMembership);
            
            _logger.LogInformation(
                "Reactivated club membership for UserId {UserId}",
                user.Id
            );
        }

        await _context.SaveChangesAsync(cancellationToken);

        // 8. ثبت تاریخچه
        var history = new ClubMembershipHistory
        {
            ClubMembershipId = clubMembership.Id,
            UserId = clubMembership.UserId,
            OldIsActive = !isNewMembership && !user.ClubMembership!.IsActive,
            NewIsActive = true,
            Action = ClubMembershipAction.Activated,
            Reason = isNewMembership
                ? $"Initial activation via contract signing - {user.PackagePurchaseMethod}"
                : $"Reactivated via contract signing - {user.PackagePurchaseMethod}",
            PerformedBy = _currentUser.GetPerformedBy()
        };

        _context.ClubMembershipHistories.Add(history);
        await _context.SaveChangesAsync(cancellationToken);

        // 9. اضافه کردن مبلغ به Pool هفته جاری
        var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
        var weeklyPool = await _context.WeeklyCommissionPools
            .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken);

        if (weeklyPool == null)
        {
            weeklyPool = new WeeklyCommissionPool
            {
                WeekDefinitionId = currentWeekDefinitionId,
                TotalPoolAmount = activationFeeValue,
                TotalBalances = 0,
                ValuePerBalance = 0,
                IsCalculated = false,
                CalculatedAt = null
            };

            await _context.WeeklyCommissionPools.AddAsync(weeklyPool, cancellationToken);
            
            _logger.LogInformation(
                "Created new WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId} with initial amount: {Amount}",
                currentWeekDefinitionId,
                activationFeeValue
            );
        }
        else
        {
            weeklyPool.TotalPoolAmount += activationFeeValue;
            _context.WeeklyCommissionPools.Update(weeklyPool);
            
            _logger.LogInformation(
                "Added {Amount} to existing WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId}. New total: {NewTotal}",
                activationFeeValue,
                currentWeekDefinitionId,
                weeklyPool.TotalPoolAmount
            );
        }

        await _context.SaveChangesAsync(cancellationToken);

        // 10. اعطای ویژگی‌های باشگاه برای کاربر (فقط برای عضویت جدید)
        if (isNewMembership)
        {
            var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
            var clubFeatures = await _context.ClubFeatures
                .Where(f => !f.IsDeleted && featureIds.Contains(f.Id))
                .ToListAsync(cancellationToken);

            if (clubFeatures.Any())
            {
                var userClubFeatures = clubFeatures.Select(feature => new UserClubFeature
                {
                    UserId = user.Id,
                    ClubMembershipId = clubMembership.Id,
                    ClubFeatureId = feature.Id,
                    GrantedAt = activationDate,
                    IsActive = true,
                    Notes = "اعطا شده هنگام امضای قرارداد"
                }).ToList();

                _context.UserClubFeatures.AddRange(userClubFeatures);
                await _context.SaveChangesAsync(cancellationToken);

                _logger.LogInformation(
                    "Granted {Count} club features to UserId {UserId}",
                    clubFeatures.Count,
                    user.Id
                );
            }
        }

        _logger.LogInformation(
            "Club membership contract accepted and activated for UserId: {UserId}, ContractId: {ContractId}, MembershipId: {MembershipId}",
            request.UserId,
            userContract.Id,
            clubMembership.Id
        );

        return new AcceptClubMembershipContractResponseDto
        {
            Success = true,
            Message = "قرارداد باشگاه مشتریان با موفقیت ثبت شد و عضویت شما فعال گردید",
            ContractId = userContract.Id
        };
    }

    /// <summary>
    /// دریافت شناسه تعریف هفته جاری
    /// </summary>
    private long GetCurrentWeekDefinitionId()
    {
        var week = _weekRepository.GetCurrentWeek();
        if (week == null)
        {
            throw new InvalidOperationException("هفته جاری در سیستم تعریف نشده است");
        }
        return week.Id;
    }

    private async Task<(bool Success, string Message)> VerifyOtpAsync(
        string mobile,
        string code,
        CancellationToken cancellationToken)
    {
        var normalizedMobile = mobile.NormalizeIranMobile();
        var now = DateTime.Now;

        var otp = await _context.OtpTokens
            .Where(o => o.Mobile == normalizedMobile 
                     && o.Purpose == OtpPurpose 
                     && !o.IsUsed 
                     && o.ExpiresAt > now)
            .OrderByDescending(o => o.Created)
            .FirstOrDefaultAsync(cancellationToken);

        if (otp == null)
        {
            return (false, "کد تایید پیدا نشد یا منقضی شده است");
        }

        if (otp.Attempts >= MaxAttempts)
        {
            return (false, "تعداد تلاش‌ها بیش از حد مجاز است. لطفاً کد جدید دریافت کنید");
        }

        otp.Attempts++;

        var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not configured");
        
        if (!_hashService.VerifyHmacSha256Hex(code, otp.CodeHash, secret))
        {
            await _context.SaveChangesAsync(cancellationToken);
            return (false, "کد تایید نادرست است");
        }

        // کد صحیح است - علامت‌گذاری به عنوان استفاده شده
        otp.IsUsed = true;
        await _context.SaveChangesAsync(cancellationToken);

        return (true, "کد تایید صحیح است");
    }
}
