using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BO.AppServer.Metadata.Dto;
using BO.Console.Commands.Base;
using Quadarax.Foundation.Core.IO.MimeTypes;
using Quadarax.Foundation.Core.QConsole;
using Quadarax.Foundation.Core.QConsole.Argument;
using Quadarax.Foundation.Core.QConsole.Attributes;
using Quadarax.Foundation.Core.QConsole.Value;
using Quadarax.Foundation.Core.Value;
using Quadarax.Foundation.Core.Value.Extensions;

// ReSharper disable InconsistentNaming

namespace BO.Console.Commands.Document
{
    [CommandDefinition]
    internal class DocumentAddCommand : AbstractWorkspaceCommand 
    {
        #region *** Constants ***
        private const string CS_CMD_DOCADD_NAME = "document_add";
        private const string CS_CMD_DOCADD_DESCR = "Add new metadocument with artifact to workspace or append artifact to existing document.";


        private const string CS_ARG_DESC_USER = "User name context. If not specified use default user context (who is logged in console).";

        private const string CS_ARG_NAME_NAME = "name";
        private const string CS_ARG_HINT_NAME = "<document_name>";
        private const string CS_ARG_DESC_NAME = "Metadata document name. If not specified use first artifact name. If document already exists, appends artifact to existing metadocument.";

        private const string CS_ARG_NAME_ARTS = "arts";
        private const string CS_ARG_HINT_ARTS = "<artifact_files>";
        private const string CS_ARG_DESC_ARTS = "Artifact file names (separated by ';') or single file name.";

        private const string CS_ARG_DESC_FORCE = "Flag if set, then appends artifact to existing metadocument, otherwise cancel operation.";

        private const string CS_ARG_NAME_OUTPUT = "setout";
        private const string CS_ARG_HINT_OUTPUT = "<is_art_output>";
        private const string CS_ARG_DESC_OUTPUT = "Flag if set, then appends artifact as output (output from PS) otherwise set as input (input from user).";

        #endregion

        #region *** Properties ***

        public override string Name => CS_CMD_DOCADD_NAME;
        public override string Description => CS_CMD_DOCADD_DESCR;


        protected string DocumentName { get; set; }
        protected IList<string> ArtifactFiles { get; set; }

        #endregion

        #region *** Constructor ***
        public DocumentAddCommand(Engine engine) : base(engine)
        {
        }
        #endregion

        #region *** EXECUTE ***
        protected override Result OnExecute()
        {
            // 1. Check if document exists
            WriteDebugInfo($"Checking existence of document '{DocumentName}' in workspace...");
            var document = Client.GetWorkspaceDocumentByNameAsync(ApiKey, ApiKeyPassword, DocumentName, UserName).Result;
            if (document!=null)
            {
                WriteDebugInfo($"Document '{DocumentName}' exists [{document.Id}] in workspace...");
            }
            else
            {
                // 2. Create new document
                WriteDebugInfo($"Document '{DocumentName}' not exists in workspace - creating...");
                document = Client.CreateWorkspaceDocumentAsync(ApiKey, ApiKeyPassword, new DocumentCDto(DocumentName), UserName).Result;
                WriteInfo($"Document '{document.Name}' [{document.Id}] created.");
            }
            // 3. Append artifacts
            WriteInfo($"Appending {ArtifactFiles.Count} artifacts...");
            var appendArtTasks = new List<Task>();
            foreach (var artFile in ArtifactFiles)
            {
                appendArtTasks.Add(Task.Factory.StartNew(async () =>
                {
                    var dtStart = DateTime.Now;
                    ArtifactRDto art;
                    await using (var fs = FileSystem.File.OpenRead(artFile))
                    {
                        art = Client.AppendWorkspaceDocumentArtifactAsync(ApiKey, ApiKeyPassword, document.Id,
                            FileSystem.Path.GetFileName(artFile), MimeTypeUtil.GetMimeType(FileSystem.Path.GetExtension(artFile)),IsForce.GetValueOrDefault(), fs, UserName).Result;
                    }
                    WriteInfo($"Artifact '{art.Name}' [{art.Id}] appended to document '{document.Name}' as mime '{art.MimeType}' @ {(DateTime.Now - dtStart).ToReadableString()}");
                }));
            }

            // 4. Sync all
            Task.WaitAll(appendArtTasks.ToArray());
            return new Result();
        }
        #endregion

        #region *** Protected overrides ***
        protected override void OnValidateArguments()
        {
            base.OnValidateArguments();
            DocumentName = GetArgumentValueOrDefault<string>(CS_ARG_NAME_NAME);

            var artifactFiles = GetArgumentValueOrDefault<string>(CS_ARG_NAME_ARTS);
            ArtifactFiles = artifactFiles.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
            var sb = new StringBuilder();
            foreach (var artFile  in ArtifactFiles)
            {
                if (!FileSystem.File.Exists(artFile))
                    sb.Append("'").Append(artFile).Append("',");
            }

            if (sb.Length > 0)
            {
                sb.Remove(sb.Length - 1, 1);
                throw new FileNotFoundException($"Artifact files: {sb} not found!");
            }

            if (string.IsNullOrEmpty(DocumentName))
                DocumentName = FileSystem.Path.GetFileNameWithoutExtension(ArtifactFiles.First());
        }

        #endregion

        #region *** Private operations ***

        protected override IEnumerable<AbstractArgument> OnSetupArguments()
        {
            var args = base.OnSetupArguments().ToList();
            args.Add(new NamedArgument(CS_ARG_NAME_NAME, CS_ARG_DESC_NAME,CS_ARG_HINT_NAME, TypeValuesEnum.String, string.Empty, false));
            args.Add(new NamedArgument(CS_ARG_NAME_ARTS, CS_ARG_DESC_ARTS,CS_ARG_HINT_ARTS, TypeValuesEnum.String, string.Empty, true));
            AppendUserArgument(args, CS_ARG_DESC_USER);

            args.Add(new FlagArgument(CS_ARG_NAME_OUTPUT, CS_ARG_DESC_OUTPUT,CS_ARG_HINT_OUTPUT, false));
            AppendForceArgument(args, CS_ARG_DESC_FORCE);
            return args.ToArray();
        }

        #endregion
    }
}