using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using BO.AppServer.Metadata.Dto;
using BO.AppServer.Metadata.Enums;
using BO.Connector;
using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
using Quadarax.Foundation.Core.Logging;

namespace Connector.PS
{
    public class Connection : AbstractConnection, IConnectionPS
    {
        #region *** Private fields ***
        protected override string ApiName => "PS";
        private string _psIdentifier;
        private IPAddress _ip;
        private int _poolSize;
        private long? _registrationId;
        #endregion


        #region *** Constructor ***
        public Connection(Uri uriApiBase, TimeSpan timeout,string psIdentifier,IPAddress ipAddress, int poolSize, ILogHandler log = null, bool isDumpContentEnabled = false) : base(uriApiBase, timeout, log, isDumpContentEnabled)
        {
            if (string.IsNullOrEmpty(psIdentifier))
                throw new ArgumentNullException(nameof(psIdentifier));
            _psIdentifier = psIdentifier;
            _ip = ipAddress ?? throw new ArgumentNullException(nameof(ipAddress));
            _poolSize = poolSize;

        }
        #endregion

        #region *** Public operations ***

        public async Task<IEnumerable<DocumentRDto>> GetDocumentsAsync(DocumentsScopeEnums scope)
        {
            if (!(scope == DocumentsScopeEnums.Pending || scope == DocumentsScopeEnums.Working))
                throw new ArgumentOutOfRangeException(
                    $"Document list scope value must be '{DocumentsScopeEnums.Working}' or '{DocumentsScopeEnums.Pending}' int this context.");
            //{ticket}/ps/{registrationId}/new
            var result = scope == DocumentsScopeEnums.Pending
                ? await CallRequestAsync<ResultsValueDto<DocumentRDto>>($"{Ticket}/queue/{_registrationId}/pending",
                    RestCallTypeEnum.Get)
                : await CallRequestAsync<ResultsValueDto<DocumentRDto>>($"{Ticket}/queue/{_registrationId}/working",
                    RestCallTypeEnum.Get);
            return result.Values;
        }

        public async Task<DocumentStatusEnum> GetDocumentStateAsync(long documentId)
        {
            //{ticket}/queue/{registrationId}/document/{documentId}/status
            var result = await CallRequestAsync<ResultValueDto<PingDto>>($"{Ticket}/queue/{_registrationId}/document/{documentId}/status", RestCallTypeEnum.Get);
            return Enum.Parse<DocumentStatusEnum>(result.Value.Status);

        }

        public async Task<DocumentRDto> GetDocumentInfoAsync(long documentId)
        {
            //{ticket}/queue/{registrationId}/document/{documentId}/info
            var result = await CallRequestAsync<ResultValueDto<DocumentRDto>>($"{Ticket}/queue/{_registrationId}/document/{documentId}/info", RestCallTypeEnum.Get);
            return result.Value;
        }

        public async Task<DocumentStatusEnum> SetDocumentStateAsync(long documentId, DocumentStatusEnum status)
        {
            if (!(status == DocumentStatusEnum.New || status == DocumentStatusEnum.Processing || status == DocumentStatusEnum.DoneOk || status == DocumentStatusEnum.DoneFail))
                throw new ArgumentOutOfRangeException(
                    $"Document status value must be '{DocumentStatusEnum.New}' or '{DocumentStatusEnum.Processing}' or '{DocumentStatusEnum.DoneOk}' or '{DocumentStatusEnum.DoneFail}' in this context.");

            var result = await CallRequestAsync<ResultValueDto<PingDto>>($"{Ticket}/queue/{_registrationId}/document/{documentId}/status/{status}", RestCallTypeEnum.Put);
            return Enum.Parse<DocumentStatusEnum>(result.Value.Status);

        }
        public async Task<Stream> GetDocumentContentAsync(long documentId, long artifactId)
        {
            var result = await CallGetStreamAsync($"{Ticket}/queue/{_registrationId}/document/{documentId}/artifact/{artifactId}");
            return result;
        }

        public async Task<ArtifactRDto> SetDocumentContentAsync(long documentId, string fileName, string mimeType, bool overwriteIfExists, Stream contentStream)
        {
            CheckIsOpen();
            var result = await CallPostStreamMultipartAsync<ResultValueDto<ArtifactRDto>>($"{Ticket}/queue/{_registrationId}/document/{documentId}",fileName,contentStream,mimeType);
            return result.Value;
        }
        #endregion


        #region *** Private operations and overrides ***
        protected override void OnOpening()
        {
            base.OnOpening();
            var result = Task.Run(() =>  CallRequestAsync<ResultValueDto<RegistrationRDto>>($"{Ticket}/register", RestCallTypeEnum.Get, GetRegistration()));
            if (!result.Result.IsSuccess)
                throw result.Result.ToAggregateException();
            Log(LogSeverityEnum.Info,$"Process server '{_psIdentifier}' [{result.Result.Value.Id}] is registered.");
        }

        private RegistrationCDto GetRegistration()
        {
            return new RegistrationCDto()
            {
                LocationIp = _ip.ToString(),
                Psinstance = _psIdentifier,
                PoolSize = _poolSize,
                Id = _registrationId.GetValueOrDefault(0)
            };
        }
        #endregion
    }
}
