using System;
using System.Collections.Generic;
using System.Text.Json;
using Dacryptero.Notifications;
using Dacryptero.Search;
using LiteDB;

namespace Dacryptero.Data
{
    public class Person : Base, ISearchable
    {
        public bool Deleted { get; set; }

        public string Role { get; set; } = "";

        [BsonRef("attribute")]
        public List<Attribute> Attributes { get; set; } = new List<Attribute>();

        [BsonIgnore]
        public SearchResultType ResultType => SearchResultType.Person;

        [BsonIgnore]
        public ConfPersonData? ConfData { get; set; }

        public List<(string name, string value)> GetInfo()
        {
            var list = new List<(string name, string value)>
            {
                ("created", Created.ToString("s")),
                ("modified", Modified.ToString("s")),
                ("role", Role),
            };
            if (Deleted)
                list.Insert(0, ("deleted", "true"));
            return list;
        }

        public virtual string GetName() => ConfData?.Name ?? $"{Role} #{Id}";

        public string? GetRole() => Role;

        public virtual IEnumerable<SearchPart> GetSearchParts()
        {
            yield return new SearchPart(FoundType.Field, "created", new BsonValue(Created));
            yield return new SearchPart(FoundType.Field, "modified", new BsonValue(Modified));
            yield return new SearchPart(FoundType.Field, "deleted", new BsonValue(Deleted));
            yield return new SearchPart(FoundType.Field, "role", new BsonValue(Role));
            if (ConfData is not null)
                foreach (var part in ConfData.GetSearchParts())
                    yield return part;
            foreach (var attr in Attributes)
            {
                yield return new SearchPart(FoundType.Attribute, attr.Key, attr.RecentEntry.Value);
            }
        }

        public void WriteTo(Utf8JsonWriter writer, string databaseName)
        {
            writer.WriteStartObject();
                writer.WriteString("id", Id.ToString());
                writer.WriteString("database", databaseName);
                writer.WriteString("created", Created);
                writer.WriteString("modified", Modified);
                writer.WriteBoolean("deleted", Deleted);
                writer.WriteString("interview", ConfData?.Interview.Id.ToString());
                writer.WriteString("name", ConfData?.Name);
                writer.WriteString("altName", Id.ToString());
                writer.WriteString("contact", ConfData?.Contact);
                writer.WriteString("role", Role);
                writer.WriteStartArray("attributes");
                foreach (var attr in Attributes)
                    attr.WriteTo(writer);
                if (ConfData is not null)
                    foreach (var attr in ConfData.Attributes)
                        attr.WriteTo(writer);
                writer.WriteEndArray();
            writer.WriteEndObject();

        }

        public void PerformIndex(DB.DBSingleController db, bool remove, bool onlyConfidential,
            ITransmitter? transmitter
        )
        {
            using var dbLock = onlyConfidential ? null : db.GetIndexLock(false, transmitter);
            using var confLock = db.GetIndexLock(true, transmitter);

            if (ConfData is not null)
            {
                db.SetIndex(IndexSource.Persons, Id, ConfData.Contact, true, remove, transmitter);
                db.SetIndex(IndexSource.Persons, Id, ConfData.Name, true, remove, transmitter);
            }

            if (!onlyConfidential)
            {
                foreach (var attr in Attributes)
                    if (attr.RecentEntry.Value.IsString)
                        db.SetIndex(IndexSource.Persons, Id, attr.RecentEntry.Value.AsString, false,
                            remove, transmitter
                        );
            }

            if (ConfData is not null)
            {
                foreach (var attr in ConfData.Attributes)
                    if (attr.RecentEntry.Value.IsString)
                        db.SetIndex(IndexSource.Persons, Id, attr.RecentEntry.Value.AsString, true,
                            remove, transmitter
                        );
            }

            GC.KeepAlive(dbLock);
            GC.KeepAlive(confLock);
        }
    }
}