using AyCode.Core.Extensions;
using AyCode.Core.Loggers;
using AyCode.Core.Server.Loggers;
using AyCode.Database.DbSets.Profiles;
using AyCode.Database.Extensions;
using AyCode.Entities;
using AyCode.Entities.Users;
using AyCode.Interfaces.Addresses;
using AyCode.Interfaces.Addresses.Dtos;
using AyCode.Interfaces.Entities;
using AyCode.Interfaces.Profiles;
using AyCode.Interfaces.Profiles.Dtos;
using AyCode.Interfaces.ServiceProviders;
using AyCode.Interfaces.Users;
using AyCode.Models.Users;
using Microsoft.EntityFrameworkCore;

namespace AyCode.Database.DbSets.Users;

public static class AcUserDbSetExtensions
{
    public static TUser? AuthenticateUser<TUser>(this IAcUserDbSetBase<TUser> ctx, string? email, string? passwordHash, bool onlyConfirmed) where TUser : class, IAcUserBase
    {
        if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(passwordHash))
            return null;

        return ctx.GetQueryableUserByEmail(email, onlyConfirmed)?.SingleOrDefault(u => u.Password == passwordHash);
    }

    public static TUser? GetUserById<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId, bool onlyConfirmed) where TUser : class, IAcUserBase
        => ctx.Users.FirstOrDefault(u => u.Id == userId && (!onlyConfirmed || u.EmailConfirmed));

    public static TUserModelDto? GetUserModelDtoById<TUserModelDto, TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId, bool onlyConfirmed) 
        where TUserModelDto : class, IAcUserModelDtoMinBase 
        where TUser : class, IAcUserBase
    {
        return ctx.GetUserById(userId, onlyConfirmed)?.ToModelDto<TUserModelDto, TUser>();
    }

    public static TUser? GetUserByEmail<TUser>(this IAcUserDbSetBase<TUser> ctx, string? email, bool onlyConfirmed) where TUser : class, IAcUserBase
        => ctx.GetQueryableUserByEmail(email, onlyConfirmed)?.FirstOrDefault();

    public static TUserModelDto? GetUserModelDtoByEmail<TUserModelDto, TUser>(this IAcUserDbSetBase<TUser> ctx, string? email, bool onlyConfirmed)
        where TUserModelDto : class, IAcUserModelDtoMinBase
        where TUser : class, IAcUserBase
    {
        return ctx.GetUserByEmail(email, onlyConfirmed)?.ToModelDto<TUserModelDto, TUser>();
    }

    public static IQueryable<TUser> GetQueryableUsersById<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId, bool onlyConfirmed) where TUser : class, IAcUserBase
        => ctx.Users.Where(u => u.Id == userId && (!onlyConfirmed || u.EmailConfirmed));

    public static IQueryable<TUser>? GetQueryableUserByEmail<TUser>(this IAcUserDbSetBase<TUser> ctx, string? email, bool onlyConfirmed) where TUser : class, IAcUserBase
    {
        GlobalLogger.Info($"GetUserByEmail: {email}");

        if (string.IsNullOrWhiteSpace(email)) return null;

        var emailLower = email.ToLower();
        return ctx.Users.Where(u => u.EmailAddress == emailLower && (!onlyConfirmed || u.EmailConfirmed));
    }

    public static bool AddUser<TUser>(this IAcUserDbSetBase<TUser> ctx, TUser user) where TUser : class, IAcUserBase
    {
        var emailLower = user.EmailAddress.ToLower();

        GlobalLogger.Info($"AddUser: {emailLower}");

        return ctx.Users.Add(user).State == EntityState.Added;
    }

    public static bool UpdateUser<TUser>(this IAcUserDbSetBase<TUser> ctx, TUser user) where TUser : class, IAcUserBase
        => ctx.Users.Update(user).State == EntityState.Modified;

    public static bool RemoveUser<TUser, TProfile, TAddress>(this IAcUserDbSetBase<TUser, TProfile, TAddress> ctx, TUser user) 
        where TUser : class, IAcUser<TProfile, TAddress>
        where TProfile : class, IAcProfile<TAddress>
        where TAddress : class, IAcAddressDtoBase
    {
        ctx.RemoveProfile(user.ProfileId);

        return ctx.Users.Remove(user).State == EntityState.Deleted;
    }

    public static bool RemoveUser<TUser, TProfile, TAddress>(this IAcUserDbSetBase<TUser, TProfile, TAddress> ctx, Guid userId) 
        where TUser : class, IAcUser<TProfile, TAddress>
        where TProfile : class, IAcProfile<TAddress>
        where TAddress : class, IAcAddressDtoBase
    {
        var user = ctx.GetUserById(userId, false);

        return user != null && ctx.RemoveUser(user);
    }

    public static bool UpdateJwtRefreshToken<TUser>(this IAcUserDbSetBase<TUser> ctx, TUser user, string refreshToken) where TUser : class, IAcUserBase
    {
        if (string.IsNullOrWhiteSpace(refreshToken)) return false;

        user.RefreshToken = refreshToken;
        ctx.Users.Update(user);

        return true;
    }

    public static TUser? UpdateJwtRefreshToken<TUser>(this IAcUserDbSetBase<TUser> ctx, string email, string refreshToken) where TUser : class, IAcUserBase
    {
        GlobalLogger.Info(@"UserDal Update refresh token");

        if (string.IsNullOrWhiteSpace(refreshToken)) return null;

        var existingUser = ctx.GetUserByEmail(email, true);
        if (existingUser == null) return null;

        existingUser.RefreshToken = refreshToken;
        ctx.Users.Update(existingUser);

        return existingUser;
    }
}