using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using InABox.Clients;

namespace InABox.Core
{

    public interface IDigitalFormDocumentHandler
    {
        void LoadDocument(Guid id, Action<byte[]> callback);
        byte[] LoadDocument(Guid id);
        Guid SaveDocument(byte[] data);
        void Run();
        void Stop();
    }

    public abstract class DigitalFormDocumentHandler : IDigitalFormDocumentHandler
    {

        private static String FileName(Guid id) => $"{id}.formdocument";
        
        protected abstract String CachePath { get; }
        
        protected abstract bool IsConnected { get; }
        
        private static Action<bool>? _status;

        public byte[] LoadDocument(Guid id)
        {
            var fullpath = Path.Combine(CachePath, FileName(id));
            if (File.Exists(fullpath))
                return File.ReadAllBytes(fullpath);

                var result = new Client<Document>().Query(
                    new Filter<Document>(x => x.FileName).IsEqualTo(FileName(id)),
                    null,
                    null
                ).Rows.FirstOrDefault()?.Get<Document,byte[]>(c=>c.Data);
                return result;
        }
        
        public void LoadDocument(Guid id, Action<byte[]> callback)
        {
            var fullpath = Path.Combine(CachePath, FileName(id));
            if (File.Exists(fullpath))
                callback(File.ReadAllBytes(fullpath));
            else
            {
                new Client<Document>().Query(
                    new Filter<Document>(x => x.FileName).IsEqualTo(FileName(id)),
                    null,
                    null,
                    CoreRange.All,
                    (o, e) =>
                    {
                        var row = o?.Rows.FirstOrDefault();
                        if (row != null)
                            callback(row.Get<Document, byte[]>(c => c.Data));
                    }
                );
            }
        }

        public Guid SaveDocument(byte[] data)
        {
            Guid result = Guid.NewGuid();
            var filename = Path.Combine(CachePath, FileName(result));
            File.WriteAllBytes(filename,data);
            return result;
        }

        private CancellationTokenSource? _cancel;

        public void Stop()
        {
            if (_cancel != null)
            {
                _cancel.Cancel();
                _cancel = null;
            }
        }
        
        protected DigitalFormDocumentHandler(Action<bool> status)
        {
            _status = status;
        }
        
        public void Run()
        {
            Stop();
            _cancel = new CancellationTokenSource();
            Task.Run(
                () =>
                {
                    bool? previouslyActive = null;
                    while (_cancel?.IsCancellationRequested != true)
                    {
                        var file = Directory.EnumerateFiles(CachePath, "*.formdocument")
                            .FirstOrDefault();

                        var isActive = !String.IsNullOrWhiteSpace(file);
                        if (isActive != previouslyActive)
                        {
                            previouslyActive = isActive;
                            _status?.Invoke(isActive);
                        }

                        if (!String.IsNullOrWhiteSpace(file) && File.Exists(file) && IsConnected)
                        {
                            var data = File.ReadAllBytes(file);
                            var document = new Document()
                            {
                                FileName = Path.GetFileName(file),
                                Data = data,
                                CRC = CoreUtils.CalculateCRC(data),
                                TimeStamp = DateTime.Now,
                                Created = DateTime.Now,
                                CreatedBy = ClientFactory.UserID
                            };
                            new Client<Document>().Save(document, "Uploaded from Mobile Device");
                            File.Delete(file);
                        }

                        Thread.Sleep(1000);
                    }
                },
                _cancel.Token
            );
        }
    }

    public static class DigitalFormDocumentFactory
    {

        private static IDigitalFormDocumentHandler? _handler;
        

        public static void Init<THandler>(THandler handler) where THandler : IDigitalFormDocumentHandler
        {
            _handler = handler;
        }

        public static void Run() => _handler?.Run();
        public static void Stop() => _handler?.Stop();
        
        public static void LoadDocument(Guid id, Action<byte[]> callback) => _handler?.LoadDocument(id, callback);
        public static byte[]? LoadDocument(Guid id) => _handler?.LoadDocument(id);
        public static Guid SaveDocument(byte[] data) => _handler?.SaveDocument(data) ?? Guid.Empty;
        
    }
}