using AyCode.Database.DbContexts.Users;
using AyCode.Interfaces.Profiles;
using AyCode.Interfaces.Users;

namespace AyCode.Database.DbSets.Users;

public static class AcUserTokenDbSetExtensions
{
    public static bool ChangePassword<TUser, TUserToken>(this IAcUserChangePasswordDbSet<TUser, TUserToken> ctx, TUser? user, string passwordHash, string verificationToken)
        where TUser : class, IAcUserBase
        where TUserToken : class, IAcUserTokenBase
    {
        if (user == null || !ctx.IsValidToken(user.Id, verificationToken)) return false;

        user.Password = passwordHash;
        ctx.Users.Update(user);

        ctx.DeactivateTokens(user.Id);

        return true;
    }

    public static TUserToken? UpdateUserTokenSent<TUserToken>(this IAcUserTokenDbSet<TUserToken> ctx, Guid userId, string verificationToken, DateTime tokenSentUtc) where TUserToken : class, IAcUserTokenBase
    {
        var userToken = ctx.GetUserToken(userId, verificationToken);

        if (userToken == null) return null;

        userToken.TokenSent = tokenSentUtc;
        userToken.TokenExpiration ??= tokenSentUtc.AddDays(1);

        var utcNow = DateTime.UtcNow;
        userToken.IsActive = tokenSentUtc <= utcNow && userToken.TokenExpiration > utcNow;

        ctx.UserTokens.Update(userToken);
        return userToken;
    }

    public static TUserToken CreateUserToken<TUserToken>(this IAcUserTokenDbSet<TUserToken> ctx, Guid userId, string verificationToken) where TUserToken : class, IAcUserTokenBase
    {
        var userToken = Activator.CreateInstance<TUserToken>();
        userToken.UserId = userId;
        userToken.Token = verificationToken;

        return ctx.CreateUserToken(userToken);
    }

    public static TUserToken CreateUserToken<TUserToken>(this IAcUserTokenDbSet<TUserToken> ctx, TUserToken userToken) where TUserToken : class, IAcUserTokenBase
    {
        ctx.DeactivateTokens(userToken.UserId);

        userToken.IsActive = true;
        ctx.UserTokens.Add(userToken);

        return userToken;
    }

    public static TUserToken? GetActiveUserToken<TUserToken>(this IAcUserTokenDbSet<TUserToken> ctx, Guid userId) where TUserToken : class, IAcUserTokenBase
        => ctx.UserTokens.SingleOrDefault(x => x.UserId == userId && x.IsActive && (x.TokenExpiration == null || x.TokenExpiration > DateTime.UtcNow));

    public static TUserToken? GetUserToken<TUserToken>(this IAcUserTokenDbSet<TUserToken> ctx, Guid userId, string verificationToken) where TUserToken : class, IAcUserTokenBase
        => ctx.UserTokens.SingleOrDefault(x => x.UserId == userId && x.Token == verificationToken);

    public static bool IsValidToken<TUserToken>(this IAcUserTokenDbSet<TUserToken> ctx, Guid userId, string verificationToken) where TUserToken : class, IAcUserTokenBase
        => ctx.UserTokens.Any(x => x.UserId == userId && x.IsActive && x.Token == verificationToken && x.TokenExpiration > DateTime.UtcNow && x.TokenSent < DateTime.UtcNow);

    public static void DeactivateTokens<TUserToken>(this IAcUserTokenDbSet<TUserToken> ctx, Guid userId) where TUserToken : class, IAcUserTokenBase
    {
        var activeUserTokens = ctx.UserTokens.Where(x => x.UserId == userId && x.IsActive);

        foreach (var activeUserToken in activeUserTokens)
            activeUserToken.IsActive = false;

        ctx.UserTokens.UpdateRange(activeUserTokens);
    }
}