using AyCode.Database.DataLayers.Users;
using AyCode.Database.DbContexts.Users;
using AyCode.Database.DbContexts;
using AyCode.Database.Tests;
using AyCode.Interfaces.Addresses;
using AyCode.Interfaces.Messages;
using AyCode.Interfaces.Profiles;
using AyCode.Interfaces.Server.Logins;
using AyCode.Interfaces.ServiceProviders;
using AyCode.Interfaces.Users;
using AyCode.Core.Consts;
using AyCode.Core.Extensions;
using AyCode.Services.Server.Logins;
using AyCode.Utils.Extensions;
using AyCode.Core.Helpers;

namespace AyCode.Services.Server.Tests.LoginServices
{
    public abstract class AcLoginServiceServerTestBase<TDal, TDbContext, TLoginServiceServer, TResultLoggedInModel, TUser, TProfile, TUserToken, TCompany, TUserToServiceProvider, TProfileAddress, TEmailMessage> : AcDatabaseTestModelBase<TDal, TDbContext>
        where TDal : AcUserDalBase<TDbContext, TUser, TProfile, TUserToken, TCompany, TUserToServiceProvider, TProfileAddress, TEmailMessage>
        where TDbContext : AcDbContextBase, IAcUserDbContextBase<TUser, TProfile, TUserToken, TCompany, TUserToServiceProvider, TProfileAddress, TEmailMessage>
        where TLoginServiceServer : class, IAcLoginServiceServer<TResultLoggedInModel, TUser, TUserToken, TProfile, TCompany, TUserToServiceProvider, TProfileAddress>
        where TResultLoggedInModel : class, IAcLoggedInModelBase<TUser, TUserToken, TProfile, TCompany, TUserToServiceProvider, TProfileAddress>
        where TUser : class, IAcUser<TProfile, TCompany, TUserToServiceProvider, TProfileAddress>
        where TProfile : class, IAcProfile<TProfileAddress>
        where TProfileAddress : class, IAcAddress
        where TUserToken : class, IAcUserTokenBase
        where TCompany : class, IAcCompanyBase
        where TUserToServiceProvider : class, IAcUserToCompanyBase
        where TEmailMessage : class, IAcEmailMessageBase
    {
        [DataTestMethod]
        [DataRow(["", "", ""])]
        public virtual async Task AcBase_RegisterUser_ReturnsUser_WhenUserExist(string[] userIdEmailPasswordStrings)
        {
            var registerUserId = Guid.Parse(userIdEmailPasswordStrings[0]);
            var registerEmail = userIdEmailPasswordStrings[1];
            var registerPassword = userIdEmailPasswordStrings[2];

            await Dal.RemoveUserAsync(registerUserId); //kitöröljük a szemetet, ha korábbról bentmaradt - J.

            var loginService = Activator.CreateInstance(typeof(TLoginServiceServer), Dal, AcEnv.AppConfiguration) as TLoginServiceServer;
            Assert.IsNotNull(loginService);

            var errorCode = await loginService.RegistrationAsync(registerUserId, registerEmail, registerPassword, null);

            Assert.IsTrue(errorCode == AcErrorCode.Unset);

            var user = Dal.GetUserByEmail(registerEmail, false);

            Assert.IsNotNull(user);
            Assert.IsNotNull(user.Profile);
            Assert.IsNotNull(user.Profile.Address);

            await Dal.RemoveUserAsync(user); //kitöröljük a szemetet - J.
        }

        [DataTestMethod]
        [DataRow(["", ""])]
        public virtual void AcBase_LoginUser_ReturnsUser_WhenUserExist(string[] emailPasswordStrings)
        {
            var loginEmail = emailPasswordStrings[0];
            var loginPassword = emailPasswordStrings[1];

            var loginService = Activator.CreateInstance(typeof(TLoginServiceServer), Dal, AcEnv.AppConfiguration) as TLoginServiceServer;
            Assert.IsNotNull(loginService);

            #region Valid email+password test

            var loggedInModel = loginService.Login(loginEmail, loginPassword);

            Assert.IsNotNull(loggedInModel);
            Assert.IsNotNull(loggedInModel.LoggedInUser);
            Assert.IsNotNull(loginService.LoggedInModel?.LoggedInUser);

            Assert.IsTrue(loggedInModel.LoginErrorCode == AcErrorCode.Unset, $"errorCode: {loggedInModel.LoginErrorCode}");
            Assert.IsTrue(loggedInModel.IsLoggedIn, $"loggedInModel.IsLoggedIn == false; errorCode: {loggedInModel.LoginErrorCode}");
            Assert.IsTrue(string.Equals(loggedInModel.LoggedInUser.EmailAddress, loginEmail, StringComparison.CurrentCultureIgnoreCase));

            #endregion Valid email+password test

            #region Wrong email test

            loggedInModel = loginService.Login("gffsdgdfg@gu.hu", loginPassword);

            Assert.IsNotNull(loggedInModel);
            Assert.IsFalse(loggedInModel.IsLoggedIn);
            Assert.IsTrue(loggedInModel.LoginErrorCode == AcErrorCode.WrongLoginData);

            #endregion Wrong email test

            #region Wrong password test

            loggedInModel = loginService.Login(loginEmail, "fsdgfsdg");

            Assert.IsNotNull(loggedInModel);
            Assert.IsFalse(loggedInModel.IsLoggedIn);
            Assert.IsTrue(loggedInModel.LoginErrorCode == AcErrorCode.WrongLoginData);

            #endregion Wrong password test
        }

        [DataTestMethod]
        [DataRow(["", "", ""])]
        public virtual void AcBase_ChangePassword_ReturnUser_WhenUserLoggedInWithNewPassword(string[] userIdOriginalPasswordNewPasswordStrings)
        {
            var userId = Guid.Parse(userIdOriginalPasswordNewPasswordStrings[0]);
            var originalPassword = userIdOriginalPasswordNewPasswordStrings[1];
            var newPassword = userIdOriginalPasswordNewPasswordStrings[2];

            var loginService = Activator.CreateInstance(typeof(TLoginServiceServer), Dal, AcEnv.AppConfiguration) as TLoginServiceServer;
            Assert.IsNotNull(loginService);

            var user = Dal.GetUserById(userId, false)!;
            RestoreOriginalPassword(loginService, user, originalPassword);

            var errorCode = loginService.ChangePassword(userId, originalPassword, newPassword);
            Assert.IsTrue(errorCode == AcErrorCode.Unset, $"{errorCode}");

            var loggedInModel = loginService.Login(user.EmailAddress, newPassword);

            Assert.IsNotNull(loggedInModel);
            
            Assert.IsTrue(loggedInModel.IsLoggedIn);
            Assert.IsTrue(loggedInModel.LoggedInUser.Id == userId);
            Assert.IsTrue(PasswordHasher.VerifyPassword(newPassword, loggedInModel.LoggedInUser.Password, PasswordHasher.GenerateDynamicSalt(userId)));

            RestoreOriginalPassword(loginService, loggedInModel.LoggedInUser, originalPassword);
        }

        protected void RestoreOriginalPassword(TLoginServiceServer loginService, TUser user, string originalPassword)
        {
            if (PasswordHasher.VerifyPassword(originalPassword, user.Password, PasswordHasher.GenerateDynamicSalt(user.Id))) return;

            loginService.UpdatePassword(user, originalPassword);
        }
    }
}