using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Dacryptero.Notifications;

namespace Dacryptero.Data.Generator
{
    public class DbDataGenerator
    {
        public DB.DBSingleController DB { get; }
        public Schema.SchemaFile Schema { get; }

        public DbDataGenerator(DB.DBSingleController db, Schema.SchemaFile file)
        {
            DB = db;
            Schema = file;
        }

        const int InterviewCount = 1000;
        const int PersonCount = 5000;

        public async Task GenerateAsync(ITransmitter transmitter)
        {
            using var job = new JobProgressNotification(transmitter,
                $"Generate {DB.Config.Name}",
                descriptionPrefix: "prepare generators..."
            );
            // create client generators
            var city = await CityGenerator.GetNameGeneratorAsync()
                .ConfigureAwait(false);
            var names = await NameGenerator.GetNameGeneratorAsync()
                .ConfigureAwait(false);
            ReadOnlyMemory<WikiMarkow> wikis = new[]
            {
                await WikiMarkow.GetWikiMarkowAsync("Germany").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Europe").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("European_Union").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Max_Planck").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Max_Planck_Society").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("History_of_agriculture").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Harry_Potter").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("The_Lord_of_the_Rings").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("J._R._R._Tolkien").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Game_of_Thrones").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Telephone").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Computer").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Konrad_Zuse").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Laptop").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Furniture").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Recycling").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Copyright").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Microsoft").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Copyright").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Gregor_Mendel").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Albert_Einstein").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Marie_Curie").ConfigureAwait(false),
                await WikiMarkow.GetWikiMarkowAsync("Google").ConfigureAwait(false),
            };
            // create normal generator
            var random = new Random();
            var interviewGen = new InterviewGenerator(wikis, names, city, random);
            var personGen = new PersonGenerator(wikis, names, city, Schema, random);
            // create interviews
            Memory<Interview> interviews = new Interview[InterviewCount];
            var limit = InterviewCount / 10;
            job.Image = "/img/svgrepo/essential-set-2/compose-83980.svg";
            job.Reset(job.Title, interviews.Length, "Interview: ");
            for (int i = 0; i < interviews.Length; ++i)
            {
                if (i % limit == 0)
                    Serilog.Log.Debug("Generating Interviews {cur}/{max} ({x}%)", i, InterviewCount, i * 100 / InterviewCount);
                job.Update(i);
                var interview = interviewGen.Generate();
                interviews.Span[i] = interview;
                await DB.AddNewInterviewAsync(interview, CancellationToken.None)
                    .ConfigureAwait(false);
            }
            // create persons
            limit = PersonCount / 100;
            job.Reset(job.Title, PersonCount, "Person: ");
            for (int i = 0; i < PersonCount; ++i)
            {
                if (i % limit == 0)
                    Serilog.Log.Debug("Generating Persons {cur}/{max} ({x}%)", i, PersonCount, i * 100 / PersonCount);
                job.Update(i);
                try
                {
                    var person = personGen.Generate();
                    person.ConfData!.Interview = interviews.Span[random.Next(interviews.Length)];
                    await DB.AddPersonAsync(person, CancellationToken.None)
                        .ConfigureAwait(false);
                    person.ConfData.Interview.Person.Add(person.Id);
                }
                catch (Exception e )
                {
                    Serilog.Log.Error(e, "Error generating person");
                }
            }
            // update person list in interviews
            job.Reset(job.Title, interviews.Length, "Update interview: ");
            for (int i = 0; i < interviews.Length; ++i)
            {
                job.Update(i);
                await DB.UpdateInterviewAsync(interviews.Span[i], CancellationToken.None)
                    .ConfigureAwait(false);
            }
        }
    }
}
