using e_suite.API.Common.repository;
using e_suite.Database.Audit;
using e_suite.Database.Core.Extensions;
using e_suite.Database.Core.Models;
using e_suite.Database.Core.Tables.Domain;
using e_suite.Database.Core.Tables.UserManager;
using e_suite.UnitTestCore;
using eSuite.Core.Clock;
using eSuite.Core.Miscellaneous;
using Microsoft.EntityFrameworkCore;
using MockQueryable;

namespace UserManager.UnitTests.Repository;

public class FakeUserManagerRepository : FakeRepository, IUserManagerRepository
{
    private readonly IClock _clock;

    public List<User> Users = [];
    public List<EmailUserAction> EmailUserActions = [];
    public List<Domain> Domains = [];
    public List<SsoProvider> SsoProviders = [];
    public List<SingleUseGuid> SingleUseGuids = [];

    public FakeUserManagerRepository(IClock clock)
    {
        _clock = clock;

        Domains.Add( new Domain
            {
                Id = 1,
                Guid = new Guid("{E5B6C043-C2EB-46CC-9387-E5F45420C93F}"),
                Name = "Default Test domain"
            }
        );

        Domains.Add(new Domain
            {
                Id = 2,
                Guid = new Guid("d3d36362-dd2a-490a-b8ef-71ee59934b8d"),
                Name = "Alternative Test Domain"
            }
        );

        SsoProviders.Add(new SsoProvider
        {
            Id = 1,
            Name = "Test provider",
            ClientId = "SecretClientId",
            ClientSecret = "NotTellingYou",
            Deleted = false,
            IsPublic = true,
            AuthorizationEndpoint = "AuthorizationEndpoint",
            TokenEndpoint = "TokenEndpoint",
            ValidIssuer = "ValidIssuer"
        });

        Users.Add( new User
        {
            Id = -1,
            Guid = new Guid("{EA6A2EB2-ADBF-47A1-8FA6-EF363D0750DC}"),
            Email = "audit@test.test",
            FirstName = "Audit",
            LastName = "Tester",
            DomainId = Domains[0].Id,
            Domain = Domains[0],
            EmailConfirmed = true,
            Created = new DateTimeOffset(2023, 7, 24, 13, 45, 24, TimeSpan.Zero),
            LastUpdated = new DateTimeOffset(2023, 7, 24, 13, 45, 25, TimeSpan.Zero)
        });
    }

    public Task<User?> GetUserByEmail(string loginEmail, CancellationToken cancellationToken)
    {
        //return Task.FromResult(Users.SingleOrDefault(x => x.Email.Equals(loginEmail, StringComparison.InvariantCultureIgnoreCase)));
        return Task.FromResult(Users.SingleOrDefault(x => x.Email.Equals(loginEmail, StringComparison.InvariantCultureIgnoreCase)));
    }

    public async Task<User?> GetUserSsoId(long ssoId, string ssoUserId, CancellationToken cancellationToken)
    {
        var users = await GetUsers()
            .Where(u => u.SsoProviderId.Equals(ssoId) && u.SsoSubject.Equals(ssoUserId))
            .ToListAsync(cancellationToken);
        return users.FirstOrDefault();
    }

    public async Task<User?> GetUserByDomainSsoId(long domainSsoId, string ssoUserId, CancellationToken cancellationToken)
    {
        var users = await GetUsers()
            .Where(u => u.Domain.SsoProviderId.Equals(domainSsoId) && u.SsoSubject.Equals(ssoUserId))
            .ToListAsync(cancellationToken);
        return users.FirstOrDefault();
    }

    public Task AddEmailUserAction(AuditUserDetails auditUserDetails, EmailUserAction emailConfirmation,
        CancellationToken cancellationToken)
    {
        emailConfirmation.User = Users.Single(x => x.Id == emailConfirmation.UserId);
        EmailUserActions.Add(emailConfirmation);
        return Task.CompletedTask;
    }

    public Task AddUser(AuditUserDetails auditUserDetails, User user, CancellationToken cancellationToken)
    {
        if (Users.Any(x => x.Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase)))
            throw new Exception("User already exists");

        Users.Add(user);
        return Task.CompletedTask;
    }

    public Task EditUser(AuditUserDetails auditUserDetails, User user, CancellationToken cancellationToken)
    {
        var realUser = Users.Single(x => x.Id == user.Id);

        realUser.FirstName = user.FirstName;
        realUser.LastName = user.LastName;
        realUser.MiddleNames = user.MiddleNames;
        realUser.Email = user.Email;
        realUser.Password = user.Password;
        realUser.EmailConfirmed = user.EmailConfirmed;
        realUser.UsingTwoFactorAuthentication = user.UsingTwoFactorAuthentication;
        realUser.TwoFactorAuthenticationKey = user.TwoFactorAuthenticationKey;
        realUser.Created = user.Created;
        realUser.Active = user.Active;
        realUser.LastUpdated = _clock.GetNow;
        return Task.CompletedTask;
    }

    public Task<EmailUserAction?> GetEmailUserAction(Guid token, CancellationToken cancellationToken)
    {
        return Task.FromResult(EmailUserActions.SingleOrDefault(c => c.Token.Equals(token)
                                                                      && c.Expires > _clock.GetNow));
    }

    public Task DeleteEmailUserAction(AuditUserDetails auditUserDetails, EmailUserAction emailUserAction, CancellationToken cancellationToken)
    {
        var item = EmailUserActions.Single(x => x.Token.Equals(emailUserAction.Token));
        EmailUserActions.Remove(item);
        return Task.CompletedTask;
    }

    public IQueryable<User> GetUsers()
    {
        return Users.BuildMock();
    }

    public User? GetUserById(IGeneralIdRef generalIdRef)
    {
        throw new NotImplementedException();
    }

    public Task<User?> GetUserByIdAsync(IGeneralIdRef generalIdRef, CancellationToken cancellationToken)
    {
        return Task.FromResult(Users.SingleOrDefault(x => x.Id == generalIdRef.Id || x.Guid == generalIdRef.Guid ));
    }

    public Task<SsoProvider?> GetSsoProviderById(long id, CancellationToken cancellationToken)
    {
        return Task.FromResult(SsoProviders.SingleOrDefault(x => x.Id == id));
    }

    public IQueryable<SsoProvider> GetSsoProviders()
    {
        return SsoProviders.BuildMock();
    }

    public Task SaveSingleUseGuidForUser(SingleUseGuid singleUseGuid, CancellationToken cancellationToken)
    {
        SingleUseGuids.Add(singleUseGuid);
        return Task.CompletedTask;
    }

    public Task<User?> GetUserBySingleUseGuid(Guid guid, CancellationToken cancellationToken)
    {
        var singleUserGuid = SingleUseGuids.SingleOrDefault(x => x.Guid == guid && x.Expires >= _clock.GetNow);
        return Task.FromResult(singleUserGuid?.User);
    }

    public Task DeleteSingleUseGuid(Guid guid, CancellationToken cancellationToken)
    {
        var singleUserGuid = SingleUseGuids.SingleOrDefault(x => x.Guid == guid && x.Expires >= _clock.GetNow);

        if (singleUserGuid != null)
        {
            SingleUseGuids.Remove(singleUserGuid);
        }
        return Task.CompletedTask;
    }

    public Task<User?> GetUserById(GeneralIdRef generalIdRef)
    {
        foreach (var user in Users)
        {
            if (user.Id == generalIdRef.Id)
                return Task.FromResult(user)!;

            if (user.Guid == generalIdRef.Guid)
                return Task.FromResult(user)!;
        }

        return Task.FromResult<User?>(null);
    }

    public Task<Domain?> GetDomainById(GeneralIdRef domainId, CancellationToken cancellationToken)
    {
        return Task.FromResult(Domains.FindByGeneralIdRef(domainId));
    }

    public Task DeleteExpiredEmailUserActions()
    {
        DeleteExpiredEmailUserActionsCalled = true;
        return Task.CompletedTask;
    }

    public Task DeleteExpiredSingleUseGuids()
    {
        DeleteExpiredSingleUseGuidsCalled = true;
        return Task.CompletedTask;

    }
    public bool DeleteExpiredSingleUseGuidsCalled { get; set; }

    public Task<EmailUserAction?> GetCurrentEmailUserAction(long userId, EmailUserActionType emailUserActionType, CancellationToken cancellationToken)
    {
        var now = _clock.GetNow;
        var result = EmailUserActions.Where(
                x => x.UserId == userId
                     && x.EmailActionType == emailUserActionType
                     && x.Expires > now)
            .OrderByDescending(x => x.Expires)
            .FirstOrDefault();

        return Task.FromResult(result);
    }

    public bool DeleteExpiredEmailUserActionsCalled { get; set; }
}