using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using LiteDB;
using System.Runtime.CompilerServices;
using async_enumerable_dotnet;

namespace Dacryptero.DB
{
    partial class DBSingleController
    {
        public async Task<DBValue<Data.Interview>?> GetInterviewAsync(ObjectId id,
            CancellationToken cancellationToken)
        {
            await mutex.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (confidential is null)
                    return null;
                Data.Interview? interview = confidential.Interview.FindById(id);
                if (interview is not null)
                    return new DBValue<Data.Interview>(this, interview);
                else return null;
            }
            finally
            {
                mutex.Release();
            }
        }
        
        public IAsyncEnumerable<DBValue<Data.Interview>> EnumerateInterviewsAsync(
            Search.SearchIndex index,
            CancellationToken cancellationToken)
        {
            if (confidential is null)
                return AsyncEnumerable.Empty<DBValue<Data.Interview>>();
            return new LockFilterEnumerable<Data.Interview, DBValue<Data.Interview>>(
                mutex,
                _ => new ThreadSafeEnumerable<Data.Interview>(
                    () => confidential.Interview.Query()
                        .Where(x => !x.Deleted)
                        .Where(index)
                        .ToEnumerable()
                ),
                (interview, content) =>
                {
                    content.Content = new DBValue<Data.Interview>(this, interview);
                    return ValueTask.FromResult(true);
                }
            );
        }

        public async Task<DBValue<bool>?> UpdateInterviewAsync(Data.Interview interview, CancellationToken cancellationToken)
        {
            await mutex.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (confidential is null)
                    return null;
                var result = confidential.Interview.Update(interview);
                if (result)
                    return new DBValue<bool>(this, true);
                else return null;
            }
            finally
            {
                mutex.Release();
            }
        }

        public async Task<Data.Interview?> AddNewInterviewAsync(CancellationToken cancellationToken)
        {
            if (confidential is null)
                return null;
            await mutex.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                var now = DateTime.UtcNow;
                var interview = new Data.Interview
                {
                    Created = now,
                    Modified = now,
                    Time = now,
                };
                confidential.Interview.Insert(interview);
                return interview;
            }
            finally
            {
                mutex.Release();
            }
        }

        public async Task<bool> AddNewInterviewAsync(Data.Interview interview, CancellationToken cancellationToken)
        {
            await mutex.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                if (confidential is not null)
                    confidential.Interview.Insert(interview);
                return confidential is not null;
            }
            finally
            {
                mutex.Release();
            }
        }
    }
}