using System;
using System.Collections.Generic;
using System.Linq;
using LiteDB;
using Dacryptero.Data.Schema;

namespace Dacryptero.Data.Generator
{
    public class PersonGenerator : DataGeneratorBase<Person>
    {
        public SchemaFile Schema { get; }

        public PersonGenerator(ReadOnlyMemory<WikiMarkow> wikiGen,
            NameGenerator nameGen, CityGenerator cityGen,
            SchemaFile schema, Random? random = null
        )
            : base(wikiGen, nameGen, cityGen, random)
        {
            Schema = schema;
        }

        public override Person Generate()
        {
            var now = DateTime.UtcNow;
            var created = GetDateTime(now.AddYears(-10), now);
            var modified = GetDateTime(created, now);
            var role = GetRole();
            var name = GetName();
            var person = new Person
            {
                Created = created,
                Modified = modified,
                Deleted = GetBool(0.05),
                Role = role.Name,
                ConfData = new ConfPersonData
                {
                    Contact = $"{name}, {GetCity()}",
                    Name = name,
                },
            };
            foreach (var (attrName, attr) in role.Attributes)
            {
                var @new = GetAttribute(created, modified, attr);
                @new.Key = attrName;
                if (attr.Protected)
                    person.ConfData.Attributes.Add(@new);
                else person.Attributes.Add(@new);
            }
            return person;
        }

        protected SchemaRole GetRole()
        {
            return Schema.CombinedRoles.Values
                .ElementAt(Random.Next(Schema.CombinedRoles.Count));
        }

        protected Attribute GetAttribute(DateTime created, DateTime modified, SchemaAttribute attribute)
        {
            modified = GetDateTime(created, modified);
            var newest = GetAttributeEntry(created, modified, attribute.Type);
            var result = new Attribute
            {
                Created = created,
                Modified = modified,
                RecentEntry = newest,
            };
            if (attribute.History)
            {
                modified = newest.Date;
                var length = Random.Next(0, 10);
                for (int i = 0; i < length; ++i)
                {
                    var entry = GetAttributeEntry(created, modified, attribute.Type);
                    result.Entries.Add(entry);
                    created = entry.Date;
                }
            }
            result.Entries.Add(newest);
            return result;
        }

        protected AttributeEntry GetAttributeEntry(DateTime created, DateTime modified, SchemaAttributeType type)
        {
            return new AttributeEntry
            {
                Date = GetDateTime(created, modified),
                Value = type switch
                {
                    SchemaAttributeType.Bool => GetBool(),
                    SchemaAttributeType.DateTime => 
                        GetDateTime(modified.AddYears(-1), modified),
                    SchemaAttributeType.Int => Random.Next(),
                    SchemaAttributeType.MultiLine =>
                        GetLongText(Random.Next(50, 5000)),
                    SchemaAttributeType.Number =>
                        Random.Next() * Random.NextDouble(),
                    SchemaAttributeType.SingleLine =>
                        GetLongText(Random.Next(10, 100))
                            .Replace('\n', ' '),
                    _ => BsonValue.Null,
                }
            };
        }
    }
}