using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Text;
using System.Text.Json;

namespace Dacryptero.Data.Generator
{
    public class NameGenerator
    {
        public ReadOnlyMemory<string> FirstNames { get; }

        public ReadOnlyMemory<string> LastNames { get; }

        public NameGenerator(ReadOnlyMemory<string> firstNames, ReadOnlyMemory<string> lastNames)
        {
            FirstNames = firstNames;
            LastNames = lastNames;
        }

        public IEnumerable<string> GenerateNames(Random? rng = null, int limit = -1)
        {
            rng ??= new Random();
            while (limit != 0)
            {
                if (limit > 0)
                    limit--;
                yield return GenerateName(rng);
            }
        }

        public string GenerateName(Random? rng = null)
        {
            rng ??= new Random();
            var sb = new StringBuilder();
            sb.Append(FirstNames.Span[rng.Next(FirstNames.Length)]);
            sb.Append(' ');
            var small = false;
            for (int i = 0; i < 2; ++i)
                if (rng.NextDouble() < 0.4)
                {
                    var next = FirstNames.Span[rng.Next(FirstNames.Length)];
                    small |= rng.NextDouble() < 0.2;
                    if (small)
                    {
                        sb.Append(next[0]);
                        sb.Append(". ");
                    }
                    else
                    {
                        sb.Append(next);
                        sb.Append(" ");
                    }
                }
                else break;
            sb.Append(LastNames.Span[rng.Next(LastNames.Length)]);
            return sb.ToString();
        }

        public static async Task<NameGenerator> GetNameGeneratorAsync()
        {
            var wc = new WebClient();
            var firstNames = new List<string>();
            Serilog.Log.Verbose("Download female first names");
            firstNames.AddRange(
                await GetNamesAsync(wc, "https://raw.githubusercontent.com/rossgoodwin/american-names/master/firstnames_f.json")
                .ConfigureAwait(false)
            );
            Serilog.Log.Verbose("Download male first names");
            firstNames.AddRange(
                await GetNamesAsync(wc, "https://raw.githubusercontent.com/rossgoodwin/american-names/master/firstnames_m.json")
                .ConfigureAwait(false)
            );
            Serilog.Log.Verbose("Download surnames");
            var lastNames = new List<string>();
            lastNames.AddRange(
                await GetNamesAsync(wc, "https://raw.githubusercontent.com/rossgoodwin/american-names/master/surnames.json")
                .ConfigureAwait(false)
            );
            return new NameGenerator(firstNames.ToArray(), lastNames.ToArray());
        }

        private static async Task<IEnumerable<string>> GetNamesAsync(WebClient webClient, string url)
        {
            var content = await webClient.DownloadDataTaskAsync(url).ConfigureAwait(false);
            var json = JsonDocument.Parse(content);
            return json.RootElement.EnumerateArray()
                .Select(x => x.GetString() ?? "")
                .Where(x => x != "");
        }
    }
}