public Task <Result> RegisterWithAgency(UserDescriptionInfo agentData, RegistrationAgencyInfo registrationAgencyInfo, string externalIdentity,
                                                string email)
        {
            return(Result.Success()
                   .Ensure(IsIdentityPresent, "User should have identity")
                   .Bind(Validate)
                   .BindWithTransaction(_context, () => Result.Success()
                                        .Bind(CreateRootAgency)
                                        .Bind(CreateAgent)
                                        .Tap(AddMasterAgentAgencyRelation))
                   .Bind(LogSuccess)
                   .Bind(SendRegistrationMailToAdmins)
                   .OnFailure(LogFailure));


            bool IsIdentityPresent() => !string.IsNullOrWhiteSpace(externalIdentity);


            Result Validate()
            => AgencyValidator.Validate(registrationAgencyInfo);


            Task <Result <AgencyInfo> > CreateRootAgency()
            => _agencyService.Create(registrationAgencyInfo, parentAgencyId: null);


            async Task <Result <(AgencyInfo, Agent)> > CreateAgent(AgencyInfo agency)
            {
                var(_, isFailure, agent, error) = await _agentService.Add(agentData, externalIdentity, email);

                return(isFailure
                    ? Result.Failure <(AgencyInfo, Agent)>(error)
                    : Result.Success((agency, agent)));
            }

            async Task AddMasterAgentAgencyRelation((AgencyInfo agency, Agent agent) agencyAgentInfo)
            {
                var(agency, agent) = agencyAgentInfo;

                // assign all roles to master agent
                var roleIds = await _context.AgentRoles.Select(x => x.Id).ToArrayAsync();

                await AddAgentAgencyRelation(agent,
                                             AgentAgencyRelationTypes.Master,
                                             agency.Id.Value,
                                             roleIds);
            }

            async Task <Result> SendRegistrationMailToAdmins(AgencyInfo agency)
            {
                var agent = $"{agentData.Title} {agentData.FirstName} {agentData.LastName}";

                if (!string.IsNullOrWhiteSpace(agentData.Position))
                {
                    agent += $" ({agentData.Position})";
                }

                var messageData = new RegistrationDataForAdmin
                {
                    Agency = new RegistrationDataForAdmin.RootAgencyRegistrationMailData
                    {
                        Id                     = agency.Id.ToString(),
                        Name                   = agency.Name,
                        CountryCode            = agency.CountryCode,
                        City                   = agency.City,
                        Address                = agency.Address,
                        Phone                  = agency.Phone,
                        PostalCode             = agency.PostalCode,
                        Fax                    = agency.Fax,
                        PreferredCurrency      = EnumFormatters.FromDescription(agency.PreferredCurrency),
                        PreferredPaymentMethod = EnumFormatters.FromDescription(agency.PreferredPaymentMethod),
                        Website                = agency.Website
                    },
                    AgentEmail = email,
                    AgentName  = agent
                };

                return(await _notificationService.Send(messageData : messageData,
                                                       notificationType : NotificationTypes.MasterAgentSuccessfulRegistration,
                                                       emails : _notificationOptions.AdministratorsEmails));
            }

            Result <AgencyInfo> LogSuccess((AgencyInfo, Agent) registrationData)
            {
                var(agency, agent) = registrationData;
                _logger.LogAgentRegistrationSuccess(agent.Email);
                return(Result.Success(agency));
            }

            void LogFailure(string error)
            {
                _logger.LogAgentRegistrationFailed(error);
            }
        }
        public Task <Result> RegisterWithCounterparty(AgentEditableInfo agentData, CounterpartyEditRequest counterpartyData, string externalIdentity,
                                                      string email)
        {
            return(Result.Success()
                   .Ensure(IsIdentityPresent, "User should have identity")
                   .BindWithTransaction(_context, () => Result.Success()
                                        .Bind(CreateCounterparty)
                                        .Bind(CreateAgent)
                                        .Tap(AddMasterCounterpartyRelation))
                   .Bind(LogSuccess)
                   .Bind(SendRegistrationMailToAdmins)
                   .OnFailure(LogFailure));

            bool IsIdentityPresent() => !string.IsNullOrWhiteSpace(externalIdentity);


            Task <Result <CounterpartyInfo> > CreateCounterparty() => _counterpartyService.Add(counterpartyData);


            async Task <Result <(CounterpartyInfo, Agent)> > CreateAgent(CounterpartyInfo counterparty)
            {
                var(_, isFailure, agent, error) = await _agentService.Add(agentData, externalIdentity, email);

                return(isFailure
                    ? Result.Failure <(CounterpartyInfo, Agent)>(error)
                    : Result.Success((counterparty1: counterparty, agent)));
            }

            async Task AddMasterCounterpartyRelation((CounterpartyInfo counterparty, Agent agent) counterpartyUserInfo)
            {
                var(counterparty, agent) = counterpartyUserInfo;
                var defaultAgency = await _counterpartyService.GetDefaultAgency(counterparty.Id);

                await AddCounterpartyRelation(agent,
                                              AgentAgencyRelationTypes.Master,
                                              PermissionSets.Master,
                                              defaultAgency.Id);
            }

            async Task <Result> SendRegistrationMailToAdmins(CounterpartyInfo counterpartyInfo)
            {
                var agent = $"{agentData.Title} {agentData.FirstName} {agentData.LastName}";

                if (!string.IsNullOrWhiteSpace(agentData.Position))
                {
                    agent += $" ({agentData.Position})";
                }

                var messageData = new RegistrationDataForAdmin
                {
                    Counterparty = counterpartyInfo,
                    AgentEmail   = email,
                    AgentName    = agent
                };

                return(await _mailSender.Send(_notificationOptions.MasterAgentMailTemplateId, _notificationOptions.AdministratorsEmails, messageData));
            }

            Result <CounterpartyInfo> LogSuccess((CounterpartyInfo, Agent) registrationData)
            {
                var(counterparty, agent) = registrationData;
                _logger.LogAgentRegistrationSuccess($"Agent {agent.Email} with counterparty '{counterparty.Name}' successfully registered");
                return(Result.Success(counterparty));
            }

            void LogFailure(string error)
            {
                _logger.LogAgentRegistrationFailed(error);
            }
        }