using CMSMicroservice.Protobuf.Protos.User;
using CMSMicroservice.Protobuf.Protos.City;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.UserCQ.Commands.CreateNewUser;
using CMSMicroservice.Application.UserCQ.Commands.UpdateUser;
using CMSMicroservice.Application.UserCQ.Commands.DeleteUser;
using CMSMicroservice.Application.UserCQ.Queries.GetUser;
using CMSMicroservice.Application.UserCQ.Queries.GetAllUserByFilter;
using CMSMicroservice.Application.UserCQ.Queries.GetJwtToken;
using CMSMicroservice.Application.UserCQ.Queries.AdminGetJwtToken;
using CMSMicroservice.Application.UserCQ.Commands.SetPasswordForUser;
using CMSMicroservice.Application.UserCQ.Commands.RefreshToken;
using CMSMicroservice.Application.UserCQ.Commands.CreateNewOtpToken;
using CMSMicroservice.Application.UserCQ.Commands.VerifyOtpToken;
using CMSMicroservice.Application.UserCQ.Commands.AcceptContract;
using CMSMicroservice.Application.UserCQ.Queries.GetCustomerProfile;
using CMSMicroservice.Application.UserCQ.Queries.GetCustomerReferrals;
using CMSMicroservice.Application.UserCQ.Queries.GetCustomerSettings;
using CMSMicroservice.Application.Common.Interfaces;
using Google.Protobuf.WellKnownTypes;
using Microsoft.EntityFrameworkCore;
using Grpc.Core;
using System.Collections.Generic;
using System.Linq;
using MediatR;
using Mapster;
using AppModels = CMSMicroservice.Application.Common.Models;

namespace CMSMicroservice.WebApi.Services;
public class UserService : UserContract.UserContractBase
{
    private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
    private readonly ISender _sender;
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUserService;
    private readonly IHashService _hashService;
    private readonly CMSMicroservice.Application.Common.FileManager.IFileManager _fileManager;
    private readonly IGenerateJwtToken _generateJwt;

    public UserService(
        IDispatchRequestToCQRS dispatchRequestToCQRS, 
        ISender sender,
        IApplicationDbContext context,
        ICurrentUserService currentUserService,
        IHashService hashService,
        CMSMicroservice.Application.Common.FileManager.IFileManager fileManager,
        IGenerateJwtToken generateJwt)
    {
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
        _sender = sender;
        _context = context;
        _currentUserService = currentUserService;
        _hashService = hashService;
        _fileManager = fileManager;
        _generateJwt = generateJwt;
    }
    public override async Task<CreateNewUserResponse> CreateNewUser(CreateNewUserRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<CreateNewUserRequest, CreateNewUserCommand, CreateNewUserResponse>(request, context);
    }
    public override async Task<Empty> UpdateUser(UpdateUserRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<UpdateUserRequest, UpdateUserCommand>(request, context);
    }
    public override async Task<Empty> DeleteUser(DeleteUserRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<DeleteUserRequest, DeleteUserCommand>(request, context);
    }
    public override async Task<GetUserResponse> GetUser(GetUserRequest request, ServerCallContext context)
    {
        // اگر Id ارسال نشده، از JWT بخون (برای کلاینت مشتری)
        if (request.Id == 0)
            request.Id = GetCurrentUserId();

        return await _dispatchRequestToCQRS.Handle<GetUserRequest, GetUserQuery, GetUserResponse>(request, context);
    }
    public override async Task<GetAllUserByFilterResponse> GetAllUserByFilter(GetAllUserByFilterRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetAllUserByFilterRequest, GetAllUserByFilterQuery, GetAllUserByFilterResponse>(request, context);
    }
    public override async Task<GetJwtTokenResponse> GetJwtToken(GetJwtTokenRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetJwtTokenRequest, GetJwtTokenQuery, GetJwtTokenResponse>(request, context);
    }
    public override async Task<AdminGetJwtTokenResponse> AdminGetJwtToken(AdminGetJwtTokenRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<AdminGetJwtTokenRequest, AdminGetJwtTokenQuery, AdminGetJwtTokenResponse>(request, context);
    }
    public override async Task<Empty> SetPasswordForUser(SetPasswordForUserRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<SetPasswordForUserRequest, SetPasswordForUserCommand>(request, context);
    }
    public override async Task<RefreshTokenResponse> RefreshToken(RefreshTokenRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<RefreshTokenRequest, RefreshTokenCommand, RefreshTokenResponse>(request, context);
    }
    
    // ============= Customer-specific Methods =============
    
    public override async Task<CreateNewOtpTokenResponse> CreateNewOtpToken(CreateNewOtpTokenRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<CreateNewOtpTokenRequest, CreateNewOtpTokenCommand, CreateNewOtpTokenResponse>(request, context);
    }
    
    public override async Task<VerifyOtpTokenResponse> VerifyOtpToken(VerifyOtpTokenRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<VerifyOtpTokenRequest, VerifyOtpTokenCommand, VerifyOtpTokenResponse>(request, context);
    }
    
    public override async Task<AcceptContractResponse> AcceptContract(AcceptContractRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<AcceptContractRequest, AcceptContractCommand, AcceptContractResponse>(request, context);
    }
    
    public override async Task<GetUserForCustomerResponse> GetUserForCustomer(GetUserForCustomerRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();
        
        var user = await _context.Users
            .AsNoTracking()
            .Include(u => u.UserContracts)
                .ThenInclude(uc => uc.Contract)
            .Include(u => u.UserRoles)
                .ThenInclude(ur => ur.Role)
            .Include(u => u.ClubMembership)
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (user == null)
            throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد"));
        
        // تولید توکن JWT با آخرین اطلاعات کاربر
        var token = await _generateJwt.GenerateJwtToken(user);
        
        return new GetUserForCustomerResponse
        {
            Id = user.Id,
            FirstName = user.FirstName ?? string.Empty,
            LastName = user.LastName ?? string.Empty,
            Mobile = user.Mobile,
            Email = user.Email ?? string.Empty,
            NationalCode = user.NationalCode ?? string.Empty,
            AvatarPath = user.AvatarPath ?? string.Empty,
            ParentId = user.NetworkParentId,
            ReferralCode = user.ReferralCode ?? string.Empty,
            IsMobileVerified = user.IsMobileVerified,
            MobileVerifiedAt = user.MobileVerifiedAt.HasValue
                ? Timestamp.FromDateTime(DateTime.SpecifyKind(user.MobileVerifiedAt.Value, DateTimeKind.Utc))
                : null,
            EmailNotifications = user.EmailNotifications,
            SmsNotifications = user.SmsNotifications,
            PushNotifications = user.PushNotifications,
            BirthDate = user.BirthDate.HasValue
                ? Timestamp.FromDateTime(DateTime.SpecifyKind(user.BirthDate.Value, DateTimeKind.Utc))
                : null,
            Token = token
        };
    }

    public override async Task<Empty> UpdateCustomerProfile(UpdateCustomerProfileRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();
        
        var user = await _context.Users
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (user == null)
            throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد"));
        
        if (request.FirstName != null) user.FirstName = request.FirstName;
        if (request.LastName != null) user.LastName = request.LastName;
        if (request.Email != null) user.Email = request.Email;
        if (request.NationalCode != null) user.NationalCode = request.NationalCode;
        if (request.BirthDate != null) user.BirthDate = request.BirthDate.ToDateTime();
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Empty();
    }

    public override async Task<GetCustomerProfileResponse> GetCustomerProfile(GetCustomerProfileRequest request, ServerCallContext context)
    {
        var query = new GetCustomerProfileQuery { UserId = 0 };
        var result = await _sender.Send(query, context.CancellationToken);
        
        return new GetCustomerProfileResponse
        {
            Id = result.Id,
            FirstName = result.FirstName,
            LastName = result.LastName,
            Mobile = result.Mobile,
            Email = result.Email,
            NationalCode = result.NationalCode,
            AvatarPath = result.AvatarPath,
            ParentId = result.ParentId,
            ReferralCode = result.ReferralCode,
            IsMobileVerified = result.IsMobileVerified,
            MobileVerifiedAt = result.MobileVerifiedAt.HasValue 
                ? Timestamp.FromDateTime(DateTime.SpecifyKind(result.MobileVerifiedAt.Value, DateTimeKind.Utc))
                : null,
            EmailNotifications = result.EmailNotifications,
            SmsNotifications = result.SmsNotifications,
            PushNotifications = result.PushNotifications,
            BirthDate = result.BirthDate.HasValue
                ? Timestamp.FromDateTime(DateTime.SpecifyKind(result.BirthDate.Value, DateTimeKind.Utc))
                : null,
            FullName = result.FullName,
            ProfileCompletionPercentage = result.ProfileCompletionPercentage
        };
    }

    public override async Task<ChangeCustomerPasswordResponse> ChangeCustomerPassword(ChangeCustomerPasswordRequest request, ServerCallContext context)
    {
        if (request.NewPassword != request.ConfirmPassword)
        {
            return new ChangeCustomerPasswordResponse
            {
                Success = false,
                Message = "رمز عبور جدید و تکرار آن یکسان نیستند"
            };
        }

        if (request.NewPassword.Length < 6)
        {
            return new ChangeCustomerPasswordResponse
            {
                Success = false,
                Message = "رمز عبور باید حداقل 6 کاراکتر باشد"
            };
        }

        var userId = GetCurrentUserId();
        
        var user = await _context.Users
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (user == null)
            throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد"));
        
        // Verify current password
        if (!string.IsNullOrEmpty(user.HashPassword))
        {
            if (!_hashService.VerifyPassword(request.CurrentPassword, user.HashPassword))
            {
                return new ChangeCustomerPasswordResponse
                {
                    Success = false,
                    Message = "رمز عبور فعلی نادرست است"
                };
            }
        }
        
        // Hash and save new password
        user.HashPassword = _hashService.HashPassword(request.NewPassword);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new ChangeCustomerPasswordResponse
        {
            Success = true,
            Message = "رمز عبور با موفقیت تغییر یافت"
        };
    }

    public override async Task<GetCustomerReferralsResponse> GetCustomerReferrals(GetCustomerReferralsRequest request, ServerCallContext context)
    {
        var query = new GetCustomerReferralsQuery
        {
            UserId = 0,
            PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
            StatusFilter = request.StatusFilter
        };
        
        var result = await _sender.Send(query, context.CancellationToken);

        var response = new GetCustomerReferralsResponse
        {
            MetaData = new MetaData
            {
                CurrentPage = result.MetaData.CurrentPage,
                TotalPage = result.MetaData.TotalPage,
                PageSize = result.MetaData.PageSize,
                TotalCount = result.MetaData.TotalCount,
                HasPrevious = result.MetaData.HasPrevious,
                HasNext = result.MetaData.HasNext
            },
            Stats = new CMSMicroservice.Protobuf.Protos.User.CustomerReferralStats
            {
                TotalReferrals = result.Stats.TotalReferrals,
                ActiveReferrals = result.Stats.ActiveReferrals,
                TotalCommissionEarned = result.Stats.TotalCommissionEarned,
                ThisMonthCommission = result.Stats.ThisMonthCommission
            }
        };

        foreach (var referral in result.Referrals)
        {
            response.Referrals.Add(new CMSMicroservice.Protobuf.Protos.User.CustomerReferralModel
            {
                Id = referral.Id,
                FirstName = referral.FirstName,
                LastName = referral.LastName,
                Mobile = referral.Mobile,
                JoinDate = Timestamp.FromDateTime(DateTime.SpecifyKind(referral.JoinDate, DateTimeKind.Utc)),
                IsActive = referral.IsActive,
                StatusMessage = referral.StatusMessage,
                Level = referral.Level,
                TotalCommission = referral.TotalCommission
            });
        }

        return response;
    }

    public override async Task<UploadCustomerAvatarResponse> UploadCustomerAvatar(UploadCustomerAvatarRequest request, ServerCallContext context)
    {
        if (request.FileData == null || request.FileData.Length == 0)
        {
            return new UploadCustomerAvatarResponse
            {
                Success = false,
                Message = "فایل انتخاب نشده است"
            };
        }

        if (request.FileData.Length > 5 * 1024 * 1024) // 5MB limit
        {
            return new UploadCustomerAvatarResponse
            {
                Success = false,
                Message = "حجم فایل نباید بیش از 5 مگابایت باشد"
            };
        }

        var allowedTypes = new[] { "image/jpeg", "image/jpg", "image/png", "image/gif" };
        if (!allowedTypes.Contains(request.FileMimeType?.ToLower()))
        {
            return new UploadCustomerAvatarResponse
            {
                Success = false,
                Message = "فرمت فایل مجاز نیست. فقط JPG, PNG و GIF مجاز هستند"
            };
        }

        var userId = GetCurrentUserId();
        
        var user = await _context.Users
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (user == null)
            throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد"));
        
        // Upload to local file manager
        var fileBytes = request.FileData.ToByteArray();
        var fileName = $"avatar_{userId}_{DateTime.UtcNow.Ticks}";
        var mime = request.FileMimeType ?? "image/jpeg";
        
        try
        {
            var result = await _fileManager.UploadImageAsync(
                "Avatars", fileBytes, mime, fileName, context.CancellationToken);
            
            // Update user avatar path in DB
            user.AvatarPath = result.Main.Path;
            await _context.SaveChangesAsync(context.CancellationToken);
            
            return new UploadCustomerAvatarResponse
            {
                Success = true,
                Message = "تصویر پروفایل با موفقیت آپلود شد",
                AvatarUrl = result.Main.Path
            };
        }
        catch (Exception ex)
        {
            return new UploadCustomerAvatarResponse
            {
                Success = false,
                Message = "خطا در آپلود فایل. لطفاً مجدد تلاش کنید"
            };
        }
    }

    public override async Task<GetCustomerSettingsResponse> GetCustomerSettings(GetCustomerSettingsRequest request, ServerCallContext context)
    {
        var query = new GetCustomerSettingsQuery { UserId = 0 };
        var result = await _sender.Send(query, context.CancellationToken);
        
        return new GetCustomerSettingsResponse
        {
            EmailNotifications = result.EmailNotifications,
            SmsNotifications = result.SmsNotifications,
            PushNotifications = result.PushNotifications,
            MarketingNotifications = result.MarketingNotifications,
            PreferredLanguage = result.PreferredLanguage,
            TimeZone = result.TimeZone,
            TwoFactorAuthEnabled = result.TwoFactorAuthEnabled
        };
    }

    public override async Task<Empty> UpdateCustomerSettings(UpdateCustomerSettingsRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();
        
        var user = await _context.Users
            .Where(u => u.Id == userId && !u.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (user == null)
            throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد"));
        
        user.EmailNotifications = request.EmailNotifications;
        user.SmsNotifications = request.SmsNotifications;
        user.PushNotifications = request.PushNotifications;
        // MarketingNotifications, PreferredLanguage, TimeZone, TwoFactorAuth
        // are stored at application level if needed in future
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Empty();
    }
    
    // ============= Helper Methods =============
    
    private long GetCurrentUserId()
    {
        if (long.TryParse(_currentUserService.UserId, out var userId) && userId > 0)
            return userId;
        
        throw new RpcException(new Status(StatusCode.Unauthenticated, "لطفاً وارد حساب کاربری خود شوید"));
    }
}
