using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using BO.AppServer.Business;
using BO.AppServer.Business.Services;
using BO.AppServer.Metadata.Dto;
using BO.AppServer.Metadata.Enums;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;

namespace BO.AppServer.Web.Services
{
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class ConsoleController : Base<ConsoleController>
    {
        private UserService _srvUsers;
        private CatalogueService _srvCatalogue;
        private WorkspaceService _srvWorkspace;
        private DocumentService _srvDocument;
        protected override RoleEnum LoginRoleAllowed => RoleEnum.Console;

        public ConsoleController(UserService srvUsers, 
            CatalogueService srvCatalogue,
            WorkspaceService srvWorkspace,
            DocumentService srvDocument,
            ILoggerFactory logger,
            AccessService srvAccess) : base(logger, srvAccess)
        {
            _srvUsers = srvUsers ?? throw new ArgumentNullException(nameof(srvUsers));
            _srvCatalogue = srvCatalogue ?? throw new ArgumentNullException(nameof(srvCatalogue));
            _srvWorkspace = srvWorkspace ?? throw new ArgumentNullException(nameof(srvWorkspace));
            _srvDocument = srvDocument ??  throw new ArgumentNullException(nameof(srvDocument));
        }
        
        #region *** User ***

        [HttpGet("{ticket}/users/{scope}")]
        public async Task<ResultsValueDto<UserRDto>> GetAllUsers(string ticket, UserScopeEnums scope, [FromHeader] string page, [FromHeader] string pageSize)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvUsers.GetAllUsersAsync(scope, new HeaderPagingDto(page, pageSize)));
        }

        [HttpGet("{ticket}/user/{identificationType}/{identificationValue}")]
        public async Task<ResultValueDto<UserRDto>> GetUser(string ticket, IdentificationTypeEnum identificationType, string identificationValue)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvUsers.GetUserAsync(identificationType, identificationValue));
        }

        [HttpPost("{ticket}/user")]
        public async Task<ResultValueDto<UserRDto>> CreateUser(string ticket, [FromBody] UserCDto user)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvUsers.CreateUserAsync(user));
        }
        #endregion

        #region *** Catalogues ***
        [HttpGet("{Ticket}/bplans/{scope}")]
        public async Task<ResultsValueDto<BillingPlanRDto>> GetAllBillingPlans(string ticket, BillingPlanScopeEnums scope, [FromHeader] string page, [FromHeader] string pageSize)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvCatalogue.GetBillingPlansAsync(scope, new HeaderPagingDto(page, pageSize)));
        }
        [HttpPost("{Ticket}/bplan")]
        public async Task<ResultValueDto<BillingPlanRDto>> CreateBillingPlan(string ticket, [FromBody] BillingPlanCDto billingPlan)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvCatalogue.CreateBillingPlanAsync(billingPlan));
        }
        [HttpPut("{Ticket}/bplan")]
        public async Task<ResultValueDto<BillingPlanRDto>> UpdateBillingPlan(string ticket, [FromBody] BillingPlanUDto billingPlan)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvCatalogue.UpdateBillingPlanAsync(billingPlan));
        }


        [HttpGet("{Ticket}/mtypes/{scope}")]
        public async Task<ResultsValueDto<MimeTypeRDto>> GetAllMimeTypes(string ticket, MimeTypeScopeEnums scope, [FromHeader] string page, [FromHeader] string pageSize)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvCatalogue.GetMimeTypesAsync(scope, new HeaderPagingDto(page, pageSize)));
        }
        [HttpPost("{Ticket}/mtype")]
        public async Task<ResultValueDto<MimeTypeRDto>> CreateMimeType(string ticket, [FromBody] MimeTypeCDto mimeType)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvCatalogue.CreateMimeTypeAsync(mimeType));
        }
        [HttpPut("{Ticket}/mtype")]
        public async Task<ResultValueDto<MimeTypeRDto>> UpdateMimeType(string ticket, [FromBody] MimeTypeUDto mimeType)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvCatalogue.UpdateMimeTypeAsync(mimeType));
        }

        #endregion

        #region *** Statistics ***
        [HttpGet("{ticket}/versions")]
        public async Task<ResultsValueDto<AssemblyInfoDto>> GetVersions(string ticket)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var assemblies = AppDomain.CurrentDomain.GetAssemblies();
                var result = new List<AssemblyInfoDto>();
                foreach (var assembly in assemblies)
                {
                    var fiv = FileVersionInfo.GetVersionInfo(assembly.Location);
                    result.Add(new AssemblyInfoDto()
                    {
                        FileVersion = fiv.FileVersion,
                        AssemblyVersion = assembly.GetName().Version?.ToString(),
                        Name = assembly.GetName().Name
                    });
                }

                return new ResultsValueDto<AssemblyInfoDto>(result);
            });
        }

        #endregion

        #region *** Workspace ***
        [HttpGet("{Ticket}/workspaces/{scope}")]
        public async Task<ResultsValueDto<WorkspaceRDto>> GetAllWorkspaces(string ticket, WorkspaceScopeEnums scope, [FromHeader] string page, [FromHeader] string pageSize, [FromHeader] string context)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvWorkspace.GetWorkspacesAsync(scope, new HeaderPagingDto(page, pageSize), context));
        }

        [HttpGet("{ticket}/workspace/{identificationType}/{identificationValue}")]
        public async Task<ResultValueDto<WorkspaceRDto>> GetWorkspace(string ticket, WorkspaceIdentificationTypeEnum identificationType, string identificationValue)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvWorkspace.GetWorkspace(identificationType, identificationValue));
        }


        [HttpPost("{ticket}/workspace")]
        public async Task<ResultValueDto<WorkspaceRDto>> CreateWorkspace(string ticket, [FromBody] WorkspaceCDto workspace)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvWorkspace.CreateWorkspaceAsync(workspace));
        }
        #endregion

        #region  *** Documents ***
        [HttpGet("{Ticket}/documents/{scope}")]
        public async Task<ResultsValueDto<DocumentRDto>> GetAllDocuments(string ticket, DocumentsScopeEnums scope, [FromHeader] string page, [FromHeader] string pageSize)
        {
            CheckAccess(ticket);
            return await Call(async () => await _srvDocument.GetAllDocumentsAsync(scope, new HeaderPagingDto(page, pageSize)));
        }
        

        [HttpGet("{Ticket}/workspace/{wrkspApiKey}/documents/{scope}")]
        public async Task<ResultsValueDto<DocumentRDto>> GetAllWorkspaceDocuments(string ticket, DocumentsScopeEnums scope,string wrkspApiKey,[FromHeader] string apiKeyPassword,  [FromHeader] string page, [FromHeader] string pageSize, [FromHeader] string context)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                return await _srvDocument.GetAllDocumentsAsync(id, scope, new HeaderPagingDto(page, pageSize));
            });
        }

        [HttpGet("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}")]
        public async Task<ResultValueDto<DocumentRDto>> GetWorkspaceDocument(string ticket, string wrkspApiKey,long documentId,[FromHeader] string apiKeyPassword,  [FromHeader] string context)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                return await _srvDocument.GetDocumentAsync(id, documentId);
            });
        }

        [HttpGet("{Ticket}/workspace/{wrkspApiKey}/document")]
        public async Task<ResultValueDto<DocumentRDto>> GetWorkspaceDocumentByName(string ticket, string wrkspApiKey,[FromHeader]string documentName,[FromHeader] string apiKeyPassword,  [FromHeader] string context)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                return await _srvDocument.GetDocumentByNameAsync(id, documentName);
            });
        }

        [HttpGet("{Ticket}/workspace/{wrkspApiKey}/document/{identificationType}/{identificationValue}")]
        public async Task<ResultValueDto<DocumentRDto>> GetWorkspaceDocumentByIdentification(string ticket, string wrkspApiKey,[FromHeader] string apiKeyPassword, IdentificationTypeEnum identificationType, string identificationValue, [FromHeader] string context)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                return await _srvDocument.GetDocumentAsync(id,identificationType,identificationValue);

            });
        }

        [HttpGet("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}/artifact/{artifactId}/content")]
        public async Task<IActionResult> GetWorkspaceDocumentArtifactContent(string ticket, string wrkspApiKey,long documentId,long artifactId,[FromHeader] string apiKeyPassword,  [FromHeader] string context)
        {
            // TODO: May wrap to better way
            try
            {
                CheckAccess(ticket);
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                var content = await _srvDocument.GetArtifactAsync(id, documentId, artifactId);
                return  new FileStreamResult(content.Item1, content.Item2);
            }
            catch (Exception e)
            {
                return Problem(e.Message);
            }
        }


        [HttpPost("{Ticket}/workspace/{wrkspApiKey}/document")]
        public async Task<ResultValueDto<DocumentRDto>> CreateWorkspaceDocument(string ticket, string wrkspApiKey,[FromHeader] string apiKeyPassword,  [FromHeader] string context, [FromBody] DocumentCDto document)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                return await _srvDocument.CreateDocumentAsync(id, document);
            });
        }

        [HttpPost("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}/artifact/input")]
        [RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
        [RequestSizeLimit(209715200)]
        //  [FromBody] ArtifactCDto artifact, 
        public async Task<ResultValueDto<ArtifactRDto>> AppendWorkspaceDocumentArtifact(string ticket, string wrkspApiKey, long documentId,[FromHeader] string apiKeyPassword, [FromHeader] string context, [FromHeader] bool overwrite,  IFormFile content)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                var artifact = _srvDocument.CreateArtifactDto(content.FileName, content.ContentType);
                await using var stream = content.OpenReadStream();
                return await _srvDocument.AppendArtifactAsync(id, documentId, artifact, overwrite, ArtifactTypeEnum.Input, stream);
            });
        }

        [HttpDelete("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}/artifact")]
        public async Task<ResultsValueDto<ArtifactRDto>> DeleteWorkspaceDocumentArtifacts(string ticket, string wrkspApiKey, long documentId,[FromHeader] string apiKeyPassword, [FromHeader] string context, [FromHeader] bool force)
        {
            CheckAccess(ticket);
            return await Call(async () =>
            {
                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                return await _srvDocument.ClearDocumentArtifactsAsync(id, documentId,force);
            });
        }

        #endregion
    }
}
