using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using AyaNova.Models;
using AyaNova.Biz;
using Microsoft.Extensions.Logging;
using Bogus;
using AyaNova.Api.ControllerHelpers;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore;


namespace AyaNova.Util
{

    public class Seeder
    {
        public int SeededUserCount = 0;
        public Faker Fake;

        public Seeder()
        {
            Fake = new Faker();
        }


        public static class Level
        {
            public enum SeedLevel { NotValid, Small, Medium, Large, Huge };
            public static SeedLevel StringToSeedLevel(string size)
            {
                switch (size.ToLowerInvariant())
                {
                    case "small":
                        return SeedLevel.Small;

                    case "medium":
                        return SeedLevel.Medium;

                    case "large":
                        return SeedLevel.Large;

                    case "huge":
                        return SeedLevel.Huge;

                    default:
                        return SeedLevel.NotValid;
                }
            }
        }



        //////////////////////////////////////////////////////
        //Seed database for trial and testing purposes
        // 

        public async Task SeedDatabaseAsync(Level.SeedLevel slevel, Decimal timeZoneOffset)
        {
            await SeedDatabaseAsync(slevel, Guid.Empty, timeZoneOffset);
        }

        public async Task SeedDatabaseAsync(Level.SeedLevel slevel, Guid JobId, Decimal timeZoneOffset)
        {
            bool LogJob = JobId != Guid.Empty;
            SeededUserCount = 0;

            ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("Seeder");
            ApiServerState apiServerState = (ApiServerState)ServiceProviderProvider.Provider.GetService(typeof(ApiServerState));

            //get the current server state so can set back to it later
            ApiServerState.ServerState wasServerState = apiServerState.GetState();
            string wasReason = apiServerState.Reason;

            try
            {
                var StatusMsg = $"Seeding data, level {slevel.ToString()}, time zone offset {timeZoneOffset.ToString()}";
                await LogStatusAsync(JobId, LogJob, log, StatusMsg);

                //Only allow this in a trial database
                if (!AyaNova.Core.License.ActiveKey.TrialLicense)
                {
                    var msg = $"This database has a registered license key so it can't be seeded: {AyaNova.Core.License.LicenseInfoLogFormat}";
                    await LogStatusAsync(JobId, LogJob, log, msg);
                    throw new System.NotSupportedException(msg);
                }

                //validate timezone offset
                if (timeZoneOffset > 14 || timeZoneOffset < (-12))
                {
                    var msg = $"Time zone offset \"{timeZoneOffset.ToString()}\" is not valid";
                    await LogStatusAsync(JobId, LogJob, log, msg);
                    throw new System.NotSupportedException(msg);
                }


                apiServerState.SetOpsOnly("Seeding database");
                //Erase all the data except for the license, schema and the SuperUser 
                await DbUtil.EmptyBizDataFromDatabaseForSeedingOrImportingAsync(log);
                //Event log erase and seeding
                using (var ct = ServiceProviderProvider.DBContext)
                {
                    await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, 0, AyaType.Global, AyaEvent.EraseAllData, "(seeding preparation)"), ct);
                    await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, 0, AyaType.Global, AyaEvent.SeedDatabase, StatusMsg), ct);
                }


                apiServerState.SetOpsOnly("Seeding database with sample data");


                //WIDGET sample form customization
                {

                    var fc = new FormCustom()
                    {
                        FormKey = AyaType.Widget.ToString(),
                        Template = @"[
                                            {
                                            ""fld"": ""Notes"",
                                            ""required"": true
                                            },                                           
                                            {
                                            ""fld"": ""WidgetCustom1"",                                           
                                            ""required"": false,
                                            ""type"": 1
                                            },
                                            {
                                            ""fld"": ""WidgetCustom2"",                                           
                                            ""required"": true,
                                            ""type"": 4
                                            },
                                            {
                                            ""fld"": ""WidgetCustom3"",                                            
                                            ""required"": false,
                                            ""type"": 5
                                            },
                                            {
                                            ""fld"": ""WidgetCustom4"",                                           
                                            ""required"": false,
                                            ""type"": 6
                                            },
                                            {
                                            ""fld"": ""WidgetCustom5"",                                            
                                            ""required"": false,
                                            ""type"": 8
                                            },
                                            {
                                            ""fld"": ""WidgetCustom6"",                                           
                                            ""required"": false,
                                            ""type"": 2
                                            },
                                            {
                                            ""fld"": ""WidgetCustom7"",                                           
                                            ""required"": false,
                                            ""type"": 3
                                            }
                                        ]"
                    };

                    //Create and save to db
                    using (var ct = ServiceProviderProvider.DBContext)
                        await FormCustomBiz.GetBiz(ct).CreateAsync(fc);
                }

                //Create a couple of DataListView's for development and testing
                {
                    var dlv = new DataListView()
                    {
                        Name = "Name starts with generic",
                        UserId = 1,
                        ListKey = "TestWidgetDataList",
                        Public = true,
                        ListView = @"[{""fld"": ""widgetname"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Generic""}]}}]"
                    };

                    //Create and save to db
                    using (var ct = ServiceProviderProvider.DBContext)
                        await DataListViewBiz.GetBiz(ct).CreateAsync(dlv);

                    dlv = new DataListView()
                    {
                        Name = "Awesome (lots of fields)",
                        UserId = 1,
                        ListKey = "TestWidgetDataList",
                        Public = true,
                        ListView = @"[{""fld"": ""widgetname"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Awesome""}]}},{""fld"":""widgetserial""},{""fld"":""widgetdollaramount""},{""fld"":""widgetusertype""},{""fld"":""widgetstartdate""},{""fld"":""widgetactive""},{""fld"":""username""},{""fld"":""widgettags""},{""fld"":""widgetcustom1""},{""fld"":""widgetcustom2""}]"
                    };

                    //Create and save to db
                    using (var ct = ServiceProviderProvider.DBContext)
                        await DataListViewBiz.GetBiz(ct).CreateAsync(dlv);
                }






                //Seed special test data for integration testing
                //log.LogInformation("Seeding known users");
                await SeedKnownUsersAsync(log);

                //log.LogInformation("Seeding all other data");
                switch (slevel)
                {
                    case Level.SeedLevel.Small:
                        {
                            #region GenSmall
                            //This is for a busy but one man shop with a single office person handling stuff back at the shop
                            //PERF                           
                            await LogStatusAsync(JobId, LogJob, log, $"Seeding SMALL number of user(s)....");
                            var watch = new Stopwatch();
                            watch.Start();
                            //Generate owner and lead tech
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.OpsAdminFull, UserType.Service);

                            //Generate one office person / secretary
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.AccountingFull, UserType.NotService);
                            //PERF
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms");


                            //100 widgets
                            watch = new Stopwatch();
                            watch.Start();
                            await GenSeedWidgetAsync(log, 100);
                            //PERF
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"100 Widgets seeded in {watch.ElapsedMilliseconds} ms");

                            #endregion gensmall
                        }
                        break;
                    case Level.SeedLevel.Medium:
                        {
                            #region GenMedium
                            //This is for a typical AyaNova medium busy shop
                            //has one location, many techs and full staff for each department
                            //PERF                           
                            await LogStatusAsync(JobId, LogJob, log, $"Seeding MEDIUM number of user(s)....");
                            var watch = new Stopwatch();
                            watch.Start();
                            //One IT administrator, can change ops but nothing else
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminFull, UserType.NotService);

                            //One business administrator, can view ops issues
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NotService);

                            //One owner who doesn't control anything but views stuff
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminLimited | AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited | AuthorizationRoles.SalesLimited, UserType.NotService);

                            //20 techs
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Service);

                            //2 subcontractors
                            await GenSeedUserAsync(log, 2, AuthorizationRoles.SubContractorFull, UserType.ServiceContractor);

                            //3 generic office people people
                            await GenSeedUserAsync(log, 3, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //2 Full sales people
                            await GenSeedUserAsync(log, 2, AuthorizationRoles.SalesFull, UserType.NotService);

                            //1 dispatch manager
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //1 Inventory manager
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NotService);

                            //1 accountant / bookkeeper
                            await GenSeedUserAsync(log, 1, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NotService);

                            //10 full on customer users
                            await GenSeedUserAsync(log, 10, AuthorizationRoles.CustomerLimited, UserType.Customer);

                            //10 limited customer users
                            await GenSeedUserAsync(log, 10, AuthorizationRoles.CustomerLimited, UserType.Customer);
                            //PERF
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms");

                            //500 widgets
                            await LogStatusAsync(JobId, LogJob, log, $"Seeding 500 Widgets....");
                            watch = new Stopwatch();
                            watch.Start();

                            //await GenSeedWidgetAsync(log, 500);
                            await GenSeedWidgetAsync(log, 500);
                            //PERF
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"500 Widgets seeded in {watch.ElapsedMilliseconds} ms");

                            #endregion genmedium
                        }
                        break;
                    case Level.SeedLevel.Large:
                        {
                            #region GenLarge
                            //this is a large corporation with multiple branches in multiple locations all in the same country
                            //Each location has a full staff and corporate head office has an overarching staff member in charge of each location

                            //PERF                           
                            await LogStatusAsync(JobId, LogJob, log, $"Seeding LARGE number of user(s)....");
                            var watch = new Stopwatch();
                            watch.Start();

                            //IT administrator, can change ops but nothing else
                            await GenSeedUserAsync(log, 2, AuthorizationRoles.OpsAdminFull, UserType.NotService);

                            //business administrator, can view ops issues
                            await GenSeedUserAsync(log, 2, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NotService);

                            //owner / upper management who doesn't control anything but views stuff
                            await GenSeedUserAsync(log, 5, AuthorizationRoles.BizAdminLimited | AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited, UserType.NotService);

                            //100 techs
                            await GenSeedUserAsync(log, 100, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Service);

                            //limited techs
                            await GenSeedUserAsync(log, 50, AuthorizationRoles.TechLimited | AuthorizationRoles.DispatchLimited, UserType.Service);

                            //20 subcontractors
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.SubContractorFull, UserType.ServiceContractor);

                            //10 limited subcontractors
                            await GenSeedUserAsync(log, 10, AuthorizationRoles.SubContractorLimited, UserType.ServiceContractor);

                            //30 generic office people people
                            await GenSeedUserAsync(log, 30, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //10 Full sales people
                            await GenSeedUserAsync(log, 10, AuthorizationRoles.SalesFull, UserType.NotService);

                            //5 Limited sales people
                            await GenSeedUserAsync(log, 5, AuthorizationRoles.SalesLimited, UserType.NotService);

                            //5 dispatch manager 
                            await GenSeedUserAsync(log, 5, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //5 Inventory manager
                            await GenSeedUserAsync(log, 5, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NotService);

                            //10 Inventory manager assistants
                            await GenSeedUserAsync(log, 5, AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //5 accountant / bookkeeper
                            await GenSeedUserAsync(log, 5, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NotService);

                            //100 full on customer users
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.CustomerFull, UserType.Customer);

                            //100 limited customer users
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.CustomerLimited, UserType.Customer);

                            //PERF
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms");

                            //5000 widgets
                            await LogStatusAsync(JobId, LogJob, log, $"Seeding 5,000 Widgets....");
                            watch = new Stopwatch();
                            watch.Start();
                            //await GenSeedWidgetAsync(log, 5000);
                            await GenSeedWidgetAsync(log, 5000);

                            //PERF
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"5k Widgets seeded in {watch.ElapsedMilliseconds} ms");
                            #endregion genlarge
                        }
                        break;

                    case Level.SeedLevel.Huge:
                        {
                            #region GenHuge
                            //this is the HUGE dataset for load and other testing
                            //It is acceptable for this seeding to take many hours as it would normally be used rarely

                            //PERF
                            await LogStatusAsync(JobId, LogJob, log, $"Seeding HUGE number of user(s)....");
                            var watch = new Stopwatch();
                            watch.Start();

                            //IT administrator, can change ops but nothing else
                            await GenSeedUserAsync(log, 10, AuthorizationRoles.OpsAdminFull, UserType.NotService);

                            //business administrator, can view ops issues
                            await GenSeedUserAsync(log, 10, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NotService);

                            //owner / upper management who doesn't control anything but views stuff
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.BizAdminLimited | AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited, UserType.NotService);

                            //regular techs                           
                            await GenSeedUserAsync(log, 500, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Service);

                            //limited techs                           
                            await GenSeedUserAsync(log, 200, AuthorizationRoles.TechLimited | AuthorizationRoles.DispatchLimited, UserType.Service);

                            //subcontractors
                            await GenSeedUserAsync(log, 80, AuthorizationRoles.SubContractorFull, UserType.ServiceContractor);

                            //limited subcontractors
                            await GenSeedUserAsync(log, 40, AuthorizationRoles.SubContractorLimited, UserType.ServiceContractor);

                            //generic office people people
                            await GenSeedUserAsync(log, 200, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //20 Full sales people
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.SalesFull, UserType.NotService);

                            //10 Limited sales people
                            await GenSeedUserAsync(log, 10, AuthorizationRoles.SalesLimited, UserType.NotService);

                            //dispatch manager 
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //Inventory manager
                            await GenSeedUserAsync(log, 40, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NotService);

                            //Inventory manager assistants
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.InventoryLimited, UserType.NotService);

                            //accountant / bookkeeper
                            await GenSeedUserAsync(log, 20, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NotService);

                            //full on customer users
                            await GenSeedUserAsync(log, 200, AuthorizationRoles.CustomerFull, UserType.Customer);

                            //limited customer users
                            await GenSeedUserAsync(log, 50, AuthorizationRoles.CustomerLimited, UserType.Customer);

                            //PERF
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"{SeededUserCount} Users seeded in {watch.ElapsedMilliseconds} ms");

                            //20000 widgets
                            await LogStatusAsync(JobId, LogJob, log, $"Seeding 20,000 Widgets....");
                            watch = new Stopwatch();
                            watch.Start();
                            //await GenSeedWidgetAsync(log, 20000);
                            await GenSeedWidgetAsync(log, 20000);
                            watch.Stop();
                            await LogStatusAsync(JobId, LogJob, log, $"20k Widgets seeded in {watch.ElapsedMilliseconds} ms");
                            #endregion genhuge
                        }
                        break;

                }

                //Seed logo files
                var LogoFilesPath = System.IO.Path.Combine(ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH, "resource", "rpt", "stock-report-templates");
                if (System.IO.Directory.Exists(LogoFilesPath))
                {
                    using (var ct = ServiceProviderProvider.DBContext)
                    {
                        var logo = await ct.Logo.FirstOrDefaultAsync();
                        if (logo == null)
                        {
                            logo = new Logo();
                            ct.Logo.Add(logo);
                            await ct.SaveChangesAsync();
                        }
                        logo.Small = System.IO.File.ReadAllBytes(System.IO.Path.Combine(LogoFilesPath, "300x100.png"));
                        logo.SmallType = "image/png";

                        logo.Medium = System.IO.File.ReadAllBytes(System.IO.Path.Combine(LogoFilesPath, "600x200.png"));
                        logo.MediumType = "image/png";

                        logo.Large = System.IO.File.ReadAllBytes(System.IO.Path.Combine(LogoFilesPath, "1200x400.png"));
                        logo.LargeType = "image/png";

                        await ct.SaveChangesAsync();
                    }
                }


                await LogStatusAsync(JobId, LogJob, log, "Seeding completed successfully");
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Seeder:SeedDatabase error during ops");
                if (LogJob)
                    await JobsBiz.LogJobAsync(JobId, $"Seeder:SeedDatabase error during ops\r\n{ex.Message}");
                throw;
            }
            finally
            {
                //TODO: fully async: watch the job and don't turn the state back until the job is done?

                log.LogInformation($"Seeder: setting server state back to {wasServerState.ToString()}");
                apiServerState.SetState(wasServerState, wasReason);
            }
        }


        //Log the status and also job if it's run via job
        private async Task LogStatusAsync(Guid JobId, bool LogJob, ILogger log, string msg)
        {
            log.LogInformation(msg);
            if (LogJob)
                await JobsBiz.LogJobAsync(JobId, msg);
        }


        public long RUNNING_COUNT = 0;
        public string Uniquify(string s)
        {
            return s + " " + (++RUNNING_COUNT).ToString();
        }


        private string[] TagSet = new[] { "red", "orange", "yellow", "green", "blue", "indigo", "violet", "brown", "black", "white", "silver", "gold", "fuchsia", "jade", "mauve", "purple", "quince", "xanthic", "zebra", "zone0", "zone1", "zone2", "zone3", "zone4", "zone5", "zone6", "zone7", "zone8", "zone9" };

        private List<string> RandomTags()
        {

            var t = Fake.PickRandom(TagSet, Fake.Random.Int(1, 5));//pick up to 5 tags to apply
            return new List<string>(t);

        }
        //////////////////////////////////////////////////////
        //Seed test data for integration tests
        //
        public async Task SeedKnownUsersAsync(ILogger log)
        {
            try
            {
                var KnownUserTags = new List<string>();
                KnownUserTags.Add("known-user");
                KnownUserTags.Add("test-role-user");
                //TEST USERS
                //one of each role type            
                await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminLimited, UserType.NotService, "BizAdminLimited", "BizAdminLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.BizAdminFull, UserType.NotService, "BizAdminFull", "BizAdminFull", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchLimited, UserType.NotService, "DispatchLimited", "DispatchLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.DispatchFull, UserType.NotService, "DispatchFull", "DispatchFull", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.InventoryLimited, UserType.NotService, "InventoryLimited", "InventoryLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.InventoryFull, UserType.NotService, "InventoryFull", "InventoryFull", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.AccountingFull, UserType.NotService, "Accounting", "Accounting", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.TechLimited, UserType.Service, "TechLimited", "TechLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.TechFull, UserType.Service, "TechFull", "TechFull", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.SalesLimited, UserType.NotService, "SalesLimited", "SalesLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.SalesFull, UserType.NotService, "SalesFull", "SalesFull", KnownUserTags);

                await GenSeedUserAsync(log, 1, AuthorizationRoles.SubContractorLimited, UserType.ServiceContractor, "SubContractorLimited", "SubContractorLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.SubContractorFull, UserType.ServiceContractor, "SubContractorFull", "SubContractorFull", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.CustomerLimited, UserType.Customer, "CustomerLimited", "CustomerLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.CustomerFull, UserType.Customer, "CustomerFull", "CustomerFull", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.CustomerFull, UserType.HeadOffice, "HeadOffice", "HeadOffice", KnownUserTags);

                await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NotService, "OpsAdminLimited", "OpsAdminLimited", KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminFull, UserType.NotService, "OpsAdminFull", "OpsAdminFull", KnownUserTags);

                //PRIVACY TEST USER - this is used for a test to see if user info leaks into the logs
                await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NotService, "TEST_PRIVACY_USER_ACCOUNT", "TEST_PRIVACY_USER_ACCOUNT", KnownUserTags);

                //TEST NOT ACTIVE - this is used for a test to see if inactive user can login
                await GenSeedUserAsync(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NotService, false, "TEST_INACTIVE", "TEST_INACTIVE", 0, KnownUserTags);

                //Alternate translation users for each stock translation
                await GenSeedUserAsync(log, 1, AuthorizationRoles.All, UserType.NotService, true, "de", "de", await TranslationBiz.TranslationNameToIdStaticAsync("de"), KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.All, UserType.NotService, true, "es", "es", await TranslationBiz.TranslationNameToIdStaticAsync("es"), KnownUserTags);
                await GenSeedUserAsync(log, 1, AuthorizationRoles.All, UserType.NotService, true, "fr", "fr", await TranslationBiz.TranslationNameToIdStaticAsync("fr"), KnownUserTags);


            }
            catch
            {
                throw;
            }
        }



        /// <summary>
        /// Generate seed user with active=true
        /// (override to save typing)       
        /// </summary>    
        public async Task GenSeedUserAsync(ILogger log, int count, AuthorizationRoles roles, UserType userType, string login, string password, List<string> tags = null)
        {
            try
            {
                await GenSeedUserAsync(log, count, roles, userType, true, login, password, 0, tags);
            }
            catch
            {
                throw;
            }
        }



        public async Task GenSeedUserAsync(ILogger log, int count, AuthorizationRoles roles, UserType userType,
        bool active = true, string login = null, string password = null, long translationId = 0, List<string> tags = null)
        {
            if (translationId == 0)
                translationId = ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID;



            for (int x = 0; x < count; x++)
            {
                User u = new User();
                u.Active = active;

                var p = new Bogus.Person();
                u.Name = Uniquify(p.FullName);
                if (login != null)
                {
                    u.Login = login;
                    u.Name += " - " + login;
                }
                else
                    u.Login = u.Name;//must be unique, name is unique

                if (password != null)
                    u.Password = password;
                else
                    u.Password = u.Login;
                u.Roles = roles;

                u.UserType = userType;
                u.EmployeeNumber = "A-" + (454 + SeededUserCount + x).ToString() + "-Y";
                u.Notes = Fake.Lorem.Sentence(null, 5);//Fake.Lorem.Paragraph(2);                            
                                                       //TODO: After have USER and HEADOFFICE and VENDOR, if usertype is subcontractor or client or headoffice it needs to set a corresponding user's parent org record id to go with it
                                                       //use provided tags or generate them
                if (tags == null)
                    u.Tags = RandomTags();
                else
                    u.Tags = tags;
                //Children and relations
                u.UserOptions = new UserOptions();
                u.UserOptions.TranslationId = translationId;
                u.UserOptions.EmailAddress = p.Email.Replace("gmail.com", "helloayanova.com").Replace("hotmail.com", "helloayanova.com").Replace("yahoo.com", "helloayanova.com");
                u.UserOptions.Hour12 = true;
                u.UserOptions.CurrencyName = "USD";
                u.UserOptions.UiColor = Fake.Internet.Color();


                //this seems wrong to get a new context inside a loop but in testing is actually faster!?
                using (AyContext ct = ServiceProviderProvider.DBContext)
                {
                    UserBiz Biz = UserBiz.GetBiz(ct);
                    //allow creation of not entirely ready users (missing client id or subcontractor vendor id etc)
                    Biz.SeedOrImportRelaxedRulesMode = true;
                    var NewObject = await Biz.CreateAsync(u);
                    if (NewObject == null)
                    {
                        log.LogError($"Seeder::GenSeedUser error creating user {u.Name}\r\n" + Biz.GetErrorsAsString());
                        throw new System.Exception("Seeder::GenSeedUser error creating user\r\n" + Biz.GetErrorsAsString());
                    }
                }
            }

            SeededUserCount += count;
        }

        //////////////////////////////////////////////////////
        //Seed widget for testing
        //
        public async Task GenSeedWidgetAsync(ILogger log, int count)
        {
            //this is 4 times slower than doing it inside the loop below
            //seems counterintuitive but maybe it's to do with the db context not being refreshed?

            //RANDOM ROLES
            Array values = Enum.GetValues(typeof(UserType));
            Random random = new Random();

            DateTime seedStartWindow = DateTime.Now.AddYears(-1);
            DateTime seedEndWindow = DateTime.Now.AddYears(1);
            for (int x = 0; x < count; x++)
            {
                Widget o = new Widget();
                o.Name = Uniquify(Fake.Commerce.ProductName());
                o.Active = true;
                DateTime dtSeed = Fake.Date.Between(seedStartWindow, seedEndWindow).ToUniversalTime();
                o.StartDate = dtSeed;
                o.EndDate = dtSeed.AddMinutes(60).ToUniversalTime();
                o.DollarAmount = Convert.ToDecimal(Fake.Commerce.Price());

                //Random but valid enum
                UserType randomUserType = (UserType)values.GetValue(random.Next(values.Length));
                o.UserType = randomUserType;
                o.Notes = Fake.Lorem.Sentence(null, 5);
                o.Tags = RandomTags();
                o.UserId = Fake.Random.Int(1, SeededUserCount);

                //RANDOM CUSTOM FIELD DATA              
                var c1 = DateUtil.UniversalISO8661Format(Fake.Date.Between(DateTime.Now.AddYears(-1), DateTime.Now.AddYears(1)));
                var c2 = Fake.Lorem.Sentence(null, 5);
                var c3 = Fake.Random.Int(1, 99999999);
                var c4 = Fake.Random.Bool().ToString().ToLowerInvariant();
                var c5 = Fake.Random.Decimal();
                o.CustomFields = $@"{{c1:""{c1}"",c2:""{c2}"",c3:{c3},c4:{c4},c5:{c5}}}";

                //This seems wrong to do in a loop but is 4 times faster this way ?!?
                using (AyContext ct = ServiceProviderProvider.DBContext)
                {
                    WidgetBiz biz = WidgetBiz.GetBiz(ct);
                    var NewObject = await biz.CreateAsync(o);
                    if (NewObject == null)
                    {
                        log.LogError($"Seeder::GenSeedWidget error creating widget {o.Name}\r\n" + biz.GetErrorsAsString());
                        throw new System.Exception("Seeder::GenSeedWidget error creating widget\r\n" + biz.GetErrorsAsString());
                    }
                }
            }
        }

    }//eoc



}//eons