using AyCode.Database.DataLayers.Users;
using AyCode.Database.DbContexts;
using AyCode.Database.DbContexts.Users;
using AyCode.Database.Tests;
using AyCode.Entities.Addresses;
using AyCode.Entities.Profiles;
using AyCode.Entities.Users;
using AyCode.Interfaces.Addresses;
using AyCode.Interfaces.Addresses.Dtos;
using AyCode.Interfaces.Messages;
using AyCode.Interfaces.Profiles;
using AyCode.Interfaces.Profiles.Dtos;
using AyCode.Interfaces.ServiceProviders;
using AyCode.Interfaces.Users;
using AyCode.Interfaces.Users.Dtos;
using AyCode.Models.Users;

namespace AyCode.Database.Tests.Users
{
    public abstract class AcUserDalTestBase<TDal, TDbContext, TUser, TProfile, TUserToken, TCompany, TUserToCompany, TAddress, TEmailMessage> : AcDatabaseTestModelBase<TDal, TDbContext>
        where TDal : AcUserDalBase<TDbContext, TUser, TProfile, TUserToken, TCompany, TUserToCompany, TAddress, TEmailMessage>
        where TDbContext : AcDbContextBase, IAcUserDbContextBase<TUser, TProfile, TUserToken, TCompany, TUserToCompany, TAddress, TEmailMessage> 
        where TUser : class, IAcUser<TProfile, TCompany, TUserToCompany, TAddress>
        where TProfile : class, IAcProfile<TAddress>
        where TAddress : class, IAcAddressDtoBase
        where TUserToken : class, IAcUserTokenBase
        where TCompany : class, IAcCompanyBase
        where TUserToCompany : class, IAcUserToCompanyBase
        where TEmailMessage : class, IAcEmailMessageBase
    {
        public class UserModelDtoDetailTest : AcUserModelDtoDetailBase<TUser, TProfile, TCompany, TUserToCompany, TAddress>
        {
            public UserModelDtoDetailTest() : base()
            {
            }

            public UserModelDtoDetailTest(IAcUserDtoDetailBase<TProfile, TCompany, TUserToCompany, TAddress> user) : base(user)
            {
            }
        }

        [DataTestMethod]
        public virtual void AcBase_GetUserById_ReturnsUser_WhenUserExists(string userIdString) => AcBase_GetUserById(userIdString);

        // ReSharper disable once MemberCanBePrivate.Global
        protected TUser AcBase_GetUserById(string userIdString)
        {
            var userId = Guid.Parse(userIdString);
            var user = Dal.GetUserById(userId, false);

            Assert.IsNotNull(user, "User is null");
            Assert.IsNotNull(user.Profile, "Profile is null");
            Assert.IsNotNull(user.Profile.Address, "Profile.Address is null");

            Assert.AreEqual(userId, user.Id);

            return user;
        }

        [DataTestMethod]
        public virtual void AcBase_GetUserByEmail_ReturnsUser_WhenUserExists(string email) => AcBase_GetUserByEmail(email);

        // ReSharper disable once MemberCanBePrivate.Global
        protected TUser AcBase_GetUserByEmail(string email)
        {
            var user = Dal.GetUserByEmail(email, false);

            Assert.IsNotNull(user, "User is null");
            Assert.IsNotNull(user.Profile, "Profile is null");
            Assert.IsNotNull(user.Profile.Address, "Profile.Address is null");

            Assert.AreEqual(email, user.EmailAddress);

            return user;
        }

        [DataTestMethod]
        public virtual void AcBase_GetUserModelDtoDetailById_ReturnsUser_WhenUserExists(string userIdString)
        {
            var userId = Guid.Parse(userIdString);
            var userModelDtoDetail = Dal.GetUserModelDtoById<UserModelDtoDetailTest>(userId, false);

            Assert.IsNotNull(userModelDtoDetail, "User is null");
            Assert.IsNotNull(userModelDtoDetail.ProfileDto, "Profile is null");
            Assert.IsNotNull(userModelDtoDetail.ProfileDto.Address, "Profile.Address is null");

            Assert.AreEqual(userId, userModelDtoDetail.Id);
        }

        protected async Task<TUser> AcBase_GetUserByEmailAsync_ReturnsUser_WhenUserExists(string email)
        {
            TUser? user = null;

            user = await Dal.GetUserByEmailAsync(email, false).ConfigureAwait(false);

            //user = await Dal.SessionAsync(ctx => ctx.Users.FirstOrDefault(x => x.EmailAddress == email)).ConfigureAwait(false);

            //await using (var ctx = Dal.Context)
            //{
            //    user = await ctx.Users.FirstOrDefaultAsync(x => x.EmailAddress == email).ConfigureAwait(false);
            //}
            
            Assert.IsNotNull(user, "User is null");
            Assert.IsNotNull(user.Profile, "Profile is null");
            Assert.IsNotNull(user.Profile.Address, "Profile.Address is null");

            Assert.AreEqual(email, user.EmailAddress);

            return user;
        }

        [DataTestMethod]
        public virtual async Task AcBase_AddUserTest(string[] userIdProfileIdAddressIdEmailStrings)
        {
            var userId = Guid.Parse(userIdProfileIdAddressIdEmailStrings[0]);
            var profileId = Guid.Parse(userIdProfileIdAddressIdEmailStrings[1]);
            var addressId = Guid.Parse(userIdProfileIdAddressIdEmailStrings[2]);
            var email = userIdProfileIdAddressIdEmailStrings[3];

            var fromAddress = "Budapest, Liszt Ferenc tér";
            var toAddress = "1211 Budapest, Kossuth Lajos utca 145";
            //var userProductToCarId = Guid.Parse("97179a87-d99f-4f12-b7b2-75e21aaec6ab");

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

            var user = Activator.CreateInstance(typeof(TUser), userId, email, "235664", "dsfglfjg45r34903t3kggvq") as TUser;
            Assert.IsNotNull(user);

            user.ProfileId = profileId;

            var profile = Activator.CreateInstance<TProfile>();
            profile.Id = profileId;;
            profile.Name = "Add user test name";

            var address = Activator.CreateInstance<TAddress>();
            address.Id = addressId;
            address.Latitude = 5362.2341652256;
            address.Longitude = 5362.2341333317;
            address.AddressText = "1214 Kossuth Lajos utca 124.";

            user.Profile = profile;
            user.Profile.AddressId = addressId;
            user.Profile.Address = address;

            Assert.IsTrue(await Dal.AddUserAsync(user));
            user = Dal.GetUserById(userId, false);
            
            Assert.IsNotNull(user);
            Assert.IsNotNull(user.Profile);
            Assert.IsNotNull(user.Profile.Address);

            Assert.IsTrue(await Dal.RemoveUserAsync(userId)); //mielőbb kitöröljük, h ne maradjon szemét a db-ben - J.

            user = Dal.GetUserById(userId, false);
            Assert.IsNull(user); //a korábbi törlés miatt NULL kell legyen - J.
        }
    }
}
