public NotifyManager(IDatabase database, INotifyMessageSender emailNotifyMessageSender = null, INotifyMessageSender phoneTextNotifyMessageSender = null, string notifyMessageTableName = "notify_message", string notifyVerifyTableName = "notify_verify", int verifyCodeExpiresSeconds = 3600, string verifyEmailFile = null, string verifyPhoneTextFile = null, string verifyCodeFormat = "###-###")
        {
            this.database = database;
            this.emailNotifyMessageEngine     = emailNotifyMessageSender == null ? null : new NotifyMessageEngine(NotifyMessageType.Email, emailNotifyMessageSender, database, notifyMessageTableName);
            this.phoneTextNotifyMessageEngine = phoneTextNotifyMessageSender == null ? null : new NotifyMessageEngine(NotifyMessageType.PhoneText, phoneTextNotifyMessageSender, database, notifyMessageTableName);
            this.notifyMessageTableName       = notifyMessageTableName;
            this.notifyVerifyTableName        = notifyVerifyTableName;
            this.verifyCodeExpiresSeconds     = verifyCodeExpiresSeconds;

            this.verifyEmailNotifyMessage = verifyEmailFile != null?NotifyMessage.ParseFile(verifyEmailFile) : null;

            this.verifyPhoneTextNotifyMessage = verifyPhoneTextFile != null?NotifyMessage.ParseFile(verifyPhoneTextFile) : null;

            this.verifyCodeFormat = verifyCodeFormat;
        }
        public async Task SendVerifyCodeAsync(string contact)
        {
            logger.Debug($"SendVerifyCodeAsync():contact={contact}");

            // Scrub contact
            string scrubbedContact = Validate(contact);

            logger.Debug($"SendVerifyCodeAsync():scrubbedContact={scrubbedContact}");

            // Generate code and expires at
            int digits = this.verifyCodeFormat.Count(x => x == '#');
            int min    = (int)Math.Pow(10, digits - 1);
            int max    = (int)Math.Pow(10, digits) - 1;
            int code   = RANDOM.Next(0, max - min) + min;

            logger.Debug($"SendVerifyCodeAsync():digits={digits},min={min},max={max},code={code}");
            DateTime expiresAt = DateTime.Now.AddSeconds(this.verifyCodeExpiresSeconds);

            // Insert/update database
            string id = await this.database.SelectValueAsync <string>($"SELECT id FROM {this.notifyVerifyTableName}", new {
                contact = scrubbedContact
            });

            using (ITransaction transaction = await this.database.BeginTransactionAsync()) {
                if (id == null)
                {
                    await transaction.InsertAsync <string>(this.notifyVerifyTableName, new {
                        contact     = scrubbedContact,
                        verify_code = code,
                        expires_at  = expiresAt,
                    });
                }
                else
                {
                    await transaction.UpdateAsync(this.notifyVerifyTableName, new {
                        id,
                        verify_code = code,
                        expires_at  = expiresAt,
                    });
                }

                // Send notify message
                var           notifyMessageType = DetectNotifyMessageType(scrubbedContact);
                NotifyMessage notifyMessage     = null;
                switch (notifyMessageType)
                {
                case NotifyMessageType.Email:
                    if (this.verifyEmailNotifyMessage == null)
                    {
                        throw new Exception("Server must be configured with verify email notify message");
                    }
                    notifyMessage = this.verifyEmailNotifyMessage;
                    break;

                case NotifyMessageType.PhoneText:
                    if (this.verifyPhoneTextNotifyMessage == null)
                    {
                        throw new Exception("Server must be configured with verify phone text notify message");
                    }
                    notifyMessage = this.verifyPhoneTextNotifyMessage;
                    break;
                }
                var evaluatedNotifyMessage = notifyMessage.Evaluate(new {
                    contact = scrubbedContact,
                    code    = code.ToString(this.verifyCodeFormat)
                });
                await this.Queue(transaction, evaluatedNotifyMessage);

                await transaction.CommitAsync();
            }
        }