using e_suite.Database.Audit;
using e_suite.Utilities.Pagination;
using eSuite.Core.Miscellaneous;
using System.Linq.Expressions;
using e_suite.API.Common;
using e_suite.API.Common.exceptions;
using e_suite.API.Common.models;
using e_suite.API.Common.repository;
using e_suite.Database.Core.Extensions;
using e_suite.Database.Core.Tables.Domain;

namespace e_suite.Modules.DomainManager
{
    public class DomainManager : IDomainManager
    {
        private readonly IDomainRepository _domainRepository;
        private readonly ISsoManagerRepository _ssoManagerRepository;

        public DomainManager(IDomainRepository domainRepository, ISsoManagerRepository soManagerRepository)
        {
            _domainRepository = domainRepository;
            _ssoManagerRepository = soManagerRepository;
        }

        public async Task<IPaginatedData<GetDomain>> GetDomainsAsync(Paging paging, CancellationToken cancellationToken)
        {
            var forms = _domainRepository.GetDomains();

            var paginatedData = await PaginatedData.Paginate<Domain, GetDomain>(forms, paging,
                KeySelector, cancellationToken);

            return paginatedData;
        }

        private Expression<Func<Domain, object>> KeySelector(string? sortKey)
        {
            return sortKey?.ToLowerInvariant() switch
            {
                "id" => x => x.Id,
                "guid" => x => x.Guid,
                "name" => x => x.Name,
                _ => x => x.Name
            };
        }

        public async Task<GetDomain> GetDomainAsync(IGeneralIdRef generalIdRef, CancellationToken cancellationToken)
        {
            var domain = await _domainRepository.GetDomainById(generalIdRef, cancellationToken);
            AssertDomain(domain);
            return new GetDomain(domain!);
        }

        public async Task CreateDomainAsync(AuditUserDetails auditUserDetails, CreateDomain createDomain,
            CancellationToken cancellationToken)
        {
            await _domainRepository.TransactionAsync(async () =>
            {
                Domain? domain = null;
                if (createDomain.Guid != null)
                {
                    domain = await _domainRepository.GetDomainById(new GeneralIdRef { Guid = createDomain.Guid },
                        cancellationToken);
                }

                domain ??= await _domainRepository.GetDomainByName(createDomain.Name, cancellationToken);

                domain = domain switch
                {
                    { Deleted: false } => throw new ExistsException("Domain already exists"),
                    null => new Domain { Guid = createDomain.Guid ?? Guid.NewGuid() },
                    _ => domain
                };

                domain.Name = createDomain.Name;
                domain.SunriseHostname = createDomain.SunriseHostName;
                domain.SunriseAppId = createDomain.SunriseAppId;
                domain.SunriseCategoryId = createDomain.SunriseCategoryId;
                domain.SigmaId = createDomain.SigmaId;

                if (createDomain.SsoProviderId != null)
                {
                    var ssoProvider =
                        await _ssoManagerRepository.GetSsoProviderAsync(createDomain.SsoProviderId, cancellationToken);
                    domain.SsoProviderId = ssoProvider?.Id;
                    domain.SsoProvider = ssoProvider;
                }

                if (!domain.Deleted)
                {
                    await _domainRepository.CreateDomainAsync(auditUserDetails, domain, cancellationToken);

                    domain = await _domainRepository.GetDomainById(new GeneralIdRef { Guid = domain.Guid }, cancellationToken);
                    await _domainRepository.AddAdministratorRoleAsync(auditUserDetails, domain!, cancellationToken);
                }
                else
                {
                    domain.Deleted = false;
                    await _domainRepository.EditDomainAsync(auditUserDetails, domain, cancellationToken);
                }
            });
        }
        private static void AssertDomain(Domain? customField)
        {
            if (customField == null || customField.Deleted)
            {
                throw new NotFoundException($"Domain not found");
            }
        }

        public async Task EditDomainAsync(AuditUserDetails auditUserDetails, EditDomain editDomain, CancellationToken cancellationToken)
        {
            await _domainRepository.TransactionAsync(async () =>
            {
                var domain = await _domainRepository.GetDomainById(editDomain.Id, cancellationToken);
                AssertDomain(domain);
                domain!.Name = editDomain.Name;

                var ssoProvider = editDomain.SsoProviderId != null
                    ? await _ssoManagerRepository.GetSsoProviderAsync(editDomain.SsoProviderId, cancellationToken)
                    : null;

                domain.SsoProviderId = ssoProvider?.Id;
                domain.SsoProvider = ssoProvider;
                domain.SunriseHostname = editDomain.SunriseHostName;
                domain.SunriseAppId = editDomain.SunriseAppId;
                domain.SunriseCategoryId = editDomain.SunriseCategoryId;
                domain.SigmaId = editDomain.SigmaId;
                
                await _domainRepository.EditDomainAsync(auditUserDetails, domain, cancellationToken);
            });
        }

        public async Task DeleteDomainAsync(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef,
            CancellationToken cancellationToken)
        {
            await _domainRepository.TransactionAsync(async () =>
            {
                var domain = await _domainRepository.GetDomainById(generalIdRef, cancellationToken);
                AssertDomain(domain);

                if (domain!.Id == 1)
                    throw new InvalidOperationException($"Not allowed to delete the {domain.Name} domain");

                domain!.Deleted = true;
                await _domainRepository.EditDomainAsync(auditUserDetails, domain, cancellationToken);
            });
        }
    }
}