using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using CMSMicroservice.Domain.Enums;

namespace CMSMicroservice.Infrastructure.Services;

public class GenerateJwtTokenService : IGenerateJwtToken
{
    private readonly IConfiguration _configuration;
    private readonly IApplicationDbContext _context;

    public GenerateJwtTokenService(IConfiguration configuration, IApplicationDbContext context)
    {
        _configuration = configuration;
        _context = context;
    }

    public async Task<string> GenerateJwtToken(User user, int? expiryDays = null)
    {
        var lastModified = user.LastModified ?? user.Created;
        var claims = new List<Claim>
        {
            new(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new(ClaimTypes.MobilePhone, user.Mobile),
            new("ReferralCode", user.ReferralCode),
            new(ClaimTypes.Name, $"{user.FirstName} {user.LastName}")
        };
        claims.Add(new Claim("MobileNumber", user.Mobile));

        claims.Add(new Claim("NationalCode", !string.IsNullOrWhiteSpace(user.NationalCode) ? user.NationalCode : ""));
        
        claims.Add(new Claim("FirstName", !string.IsNullOrWhiteSpace(user.FirstName) ? user.FirstName : ""));

        claims.Add(new Claim("LastName", !string.IsNullOrWhiteSpace(user.LastName) ? user.LastName : ""));

        if (user.UserContracts.Any(a => a.Contract.Type == ContractType.Main))
            claims.Add(new Claim("IsSignMainContract", true.ToString()));
        else
            claims.Add(new Claim("IsSignMainContract", false.ToString()));

        // ClubMembership Claims
        if (user.ClubMembership != null)
        {
            claims.Add(new Claim("IsClubMemberActive", user.ClubMembership.IsActive.ToString()));
            if (user.ClubMembership.LastModified.HasValue && user.ClubMembership.LastModified.Value > lastModified)
                lastModified = user.ClubMembership.LastModified.Value;
        }
        else
        {
            claims.Add(new Claim("IsClubMemberActive", "false"));
        }

        // Package Purchase Status
        claims.Add(new Claim("HasPurchasedPackage", 
            (user.PackagePurchaseMethod != PackagePurchaseMethod.None).ToString()));

        var hasAddress = await _context.UserAddresses
            .AnyAsync(a => a.UserId == user.Id && !a.IsDeleted);
        claims.Add(new Claim("HasAddress", hasAddress.ToString()));

        if (user.UserRoles != null && user.UserRoles.Any())
        {
            foreach (var userRole in user.UserRoles)
            {
                claims.Add(new Claim(ClaimTypes.Role, userRole.Role.Name));
            }

            if (user.UserRoles.Max(x => x.Created) > lastModified)
                lastModified = user.UserRoles.Max(x => x.Created);
            if (user.UserRoles.Where(x => x.LastModified != null).Max(x => x.LastModified) > lastModified)
                lastModified = user.UserRoles.Where(x => x.LastModified != null).Max(x => x.Created);
        }

        claims.Add(new Claim("LastModified", lastModified.ToString()));

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var expiry = expiryDays != null
            ? DateTime.Now.AddDays(Convert.ToInt32(expiryDays))
            : DateTime.Now.AddDays(Convert.ToInt32(_configuration["JwtExpiryInDays"]));

        var token = new JwtSecurityToken(
            _configuration["JwtIssuer"],
            _configuration["JwtAudience"],
            claims,
            expires: expiry,
            signingCredentials: creds
        );
        var _token = new JwtSecurityTokenHandler().WriteToken(token);
        return _token;
    }
}