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

namespace BO.Connector.Console
{
    public class Connection : AbstractConnection
    {
        #region *** Private fields ***
        protected override string ApiName => "Console";
        #endregion

        #region *** Constructors ***
        public Connection(Uri uriApiBase, TimeSpan timeout, ILogHandler log = null, bool isDumpContentEnabled = false) : base(uriApiBase, timeout, log, isDumpContentEnabled)
        {
        }
        #endregion

        #region *** CRUD User ***
        public async Task<UserRDto> GetUserAsync(IdentificationTypeEnum identificationType, string identificationValue)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultValueDto<UserRDto>>($"{Ticket}/user/{identificationType}/{identificationValue}", RestCallTypeEnum.Get);
            return result.Value;
        }

        public async Task<IEnumerable<UserRDto>> GetUsersAsync(UserScopeEnums scope, PagingDto paging = null)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultsValueDto<UserRDto>>($"{Ticket}/users/{scope}", RestCallTypeEnum.Get,null, GetPagingHeaderAttributes(paging));
            return result.Values;
        }

        public async Task<UserRDto> CreateUserAsync(UserCDto newUser)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultValueDto<UserRDto>>($"{Ticket}/user", RestCallTypeEnum.Post, newUser);
            return result.Value;
        }

        public async Task<UserRDto> UpdateUserAsnc(IdentificationTypeEnum identificationType, string identificationValue, UserUDto updateUser)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultValueDto<UserRDto>>($"{Ticket}/user/{identificationType}/{identificationValue}", RestCallTypeEnum.Put, updateUser);
            return result.Value;
        }

        public async Task<UserRDto> DeleteUserAsync(IdentificationTypeEnum identificationType, string identificationValue)
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultValueDto<UserRDto>>($"{Ticket}/user/{identificationType}/{identificationValue}", RestCallTypeEnum.Delete);
            return result.Value;
        }
        #endregion

        #region *** Statistics ***
        public async Task<IEnumerable<StatisticsDto>> GetStatisticsAsync(StatisticsScopeEnums scope, PagingDto paging = null)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultsValueDto<StatisticsDto>>($"{Ticket}/statistics/{scope}", RestCallTypeEnum.Get,null,GetPagingHeaderAttributes(paging));
            return result.Values;
        }
        public async Task<IEnumerable<AssemblyInfoDto>> GetVersionsAsync()
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultsValueDto<AssemblyInfoDto>>($"{Ticket}/versions", RestCallTypeEnum.Get);
            return result.Values;
        }
        #endregion

        #region *** CRUD BillingPlan ***
        public async Task<BillingPlanRDto> CreateBillingPlanAsync(BillingPlanCDto newBillingPlan)
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultValueDto<BillingPlanRDto>>($"{Ticket}/bplan", RestCallTypeEnum.Post,newBillingPlan);
            return result.Value;
        }

        public async Task<BillingPlanRDto> UpdateBillingPlanAsync(BillingPlanUDto updBillingPlan)
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultValueDto<BillingPlanRDto>>($"{Ticket}/bplan", RestCallTypeEnum.Put,updBillingPlan);
            return result.Value;
        }


        public async Task<IEnumerable<BillingPlanRDto>> GetBillingPlansAsync(BPlanScopeEnums scope, PagingDto paging = null)
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultsValueDto<BillingPlanRDto>>($"{Ticket}/bplans/{scope}", RestCallTypeEnum.Get,null,GetPagingHeaderAttributes(paging));
            return result.Values;
        }
        #endregion

        #region *** CRUD MIME Type ***
        public async Task<MimeTypeRDto> CreateMimeTypeAsync(MimeTypeCDto newMimeType)
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultValueDto<MimeTypeRDto>>($"{Ticket}/mtype", RestCallTypeEnum.Post,newMimeType);
            return result.Value;
        }

        public async Task<MimeTypeRDto> UpdateMimeTypeAsync(MimeTypeUDto updMimeType)
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultValueDto<MimeTypeRDto>>($"{Ticket}/mtype", RestCallTypeEnum.Put,updMimeType);
            return result.Value;
        }

        public async Task<IEnumerable<MimeTypeRDto>> GetMimeTypesAsync(MimeTypeScopeEnums scope, PagingDto paging = null)
        {
            CheckIsOpen();
            var result =  await CallRequestAsync<ResultsValueDto<MimeTypeRDto>>($"{Ticket}/mtypes/{scope}", RestCallTypeEnum.Get,null,GetPagingHeaderAttributes(paging));
            return result.Values;
        }
        #endregion

        #region *** Documents ***
        public async Task<IEnumerable<DocumentRDto>> GetDocumentsAsync(DocumentsScopeEnums scope, PagingDto paging = null)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultsValueDto<DocumentRDto>>($"{Ticket}/documents/{scope}", RestCallTypeEnum.Get,null,GetPagingHeaderAttributes(paging));
            return result.Values;
        }

        public async Task<IEnumerable<DocumentRDto>> GetWorkspaceDocumentsAsync(DocumentsScopeEnums scope, string workspaceApiKey,string apiKeyPassword,string userNameContext, PagingDto paging = null)
        {
            CheckIsOpen();
            var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword,GetPagingHeaderAttributes(paging)));
            var result = await CallRequestAsync<ResultsValueDto<DocumentRDto>>($"{Ticket}/workspace/{workspaceApiKey}/documents/{scope}", RestCallTypeEnum.Get,null, header);
            return result.Values;
        }

        public async Task<DocumentRDto> GetWorkspaceDocumentByNameAsync(string workspaceApiKey,string apiKeyPassword,string documentName, string userNameContext)
        {
            CheckIsOpen();
            var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword));
            header.Add("documentName", documentName);
            var result = await CallRequestAsync<ResultValueDto<DocumentRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document", RestCallTypeEnum.Get,null, header);
            // can return null value
            return result.HasNoDataException() ? null : result.Value;
        }

        public async Task<DocumentRDto> GetWorkspaceDocumentAsync(string workspaceApiKey,string apiKeyPassword,IdentificationTypeEnum identificationType, string identificationValue, string userNameContext)
        {
            CheckIsOpen();
            var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword));
            var result = await CallRequestAsync<ResultValueDto<DocumentRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document/{identificationType}/{identificationValue}", RestCallTypeEnum.Get,null, header);
            return result.Value;
        }

        public async Task<Stream> GetWorkspaceDocumentContentAsync(string workspaceApiKey,string apiKeyPassword,long documentId, long artifactId, string userNameContext)
        {
            CheckIsOpen();
            var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword));
            var result = await CallGetStreamAsync($"{Ticket}/workspace/{workspaceApiKey}/document/{documentId}/artifact/{artifactId}/content", header);
            return result;
        }

        public async Task<DocumentRDto> CreateWorkspaceDocumentAsync(string workspaceApiKey,string apiKeyPassword,DocumentCDto data, string userNameContext)
        {
            CheckIsOpen();
            var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword));
            var result = await CallRequestAsync<ResultValueDto<DocumentRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document", RestCallTypeEnum.Post,data, header);
            return result.Value;
        }

        public async Task<ArtifactRDto> AppendWorkspaceDocumentArtifactAsync(string workspaceApiKey,string apiKeyPassword,long documentId, string fileName,string mimeType,bool overwriteIfExists, Stream contentStream, string userNameContext)
        {
            CheckIsOpen();
            var header = GetOverwriteHeaderAttributes(overwriteIfExists,GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword)));
            var result = await CallPostStreamMultipartAsync<ResultValueDto<ArtifactRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document/{documentId}/artifact/input", fileName, contentStream,mimeType, header);
            return result.Value;
        }
        
        public async Task<IEnumerable<ArtifactRDto>> ClearWorkspaceDocumentArtifactAsync(string workspaceApiKey,string apiKeyPassword,long documentId,bool force, string userNameContext)
        {
            CheckIsOpen();
            var header = GetForceHeaderAttributes(force,GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword)));
            var result = await CallRequestAsync<ResultsValueDto<ArtifactRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document/{documentId}/artifact",RestCallTypeEnum.Delete,null, header);
            return result.Values;
        }


        #endregion


        #region *** Workspace ***
        public async Task<WorkspaceRDto> CreateWorkspaceAsync(WorkspaceCDto workspace)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultValueDto<WorkspaceRDto>>($"{Ticket}/workspace", RestCallTypeEnum.Post,workspace);
            return result.Value;
        }

        public async Task<WorkspaceRDto> GetWorkspaceAsync(WorkspaceIdentificationTypeEnum identificationType, string identificationValue)
        {
            CheckIsOpen();
            var result = await CallRequestAsync<ResultValueDto<WorkspaceRDto>>($"{Ticket}/workspace/{identificationType}/{identificationValue}", RestCallTypeEnum.Get);
            return result.Value;
        }
        public async Task<WorkspaceRDto> GetWorkspaceAsync(IdentificationTypeEnum identificationType, string identificationValue)
        {
            var idType = WorkspaceIdentificationTypeEnum.Id;
            switch (identificationType)
            {
                case IdentificationTypeEnum.Id:
                    break;
                case IdentificationTypeEnum.Name:
                    idType = WorkspaceIdentificationTypeEnum.Name;
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(identificationType));
            }
            return await GetWorkspaceAsync(idType , identificationValue);
        }

        public async Task<IEnumerable<WorkspaceRDto>> GetWorkspacesAsync(WorkspaceScopeEnums scope,string userNameContext, PagingDto paging = null)
        {
            CheckIsOpen();
            var headers = GetUserContextHeaderAttributes(userNameContext, GetPagingHeaderAttributes(paging));
            var result = await CallRequestAsync<ResultsValueDto<WorkspaceRDto>>($"{Ticket}/workspaces/{scope}", RestCallTypeEnum.Get,null,headers);
            return result.Values;
        }

        #endregion

    }
}
