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

namespace BO.AppServer.Web.Services
{
    public abstract class Base<TController>  : ControllerBase
        where TController : ControllerBase
    {
        protected abstract RoleEnum LoginRoleAllowed { get; }
        protected AccessService _srvAccess;

        private ILogger<TController> _logger;

        protected Base(ILoggerFactory logger, AccessService srvAccess)
        {
            _logger = logger.CreateLogger<TController>() ?? throw new ArgumentNullException(nameof(logger));
            _srvAccess = srvAccess ?? throw new ArgumentNullException(nameof(srvAccess));
        }

        [AllowAnonymous]
        [HttpGet("{magicKey}/auth")]
        public async Task<ResultValueDto<AuthDto>> Authorize(string magicKey)
        {
            _srvAccess.OpenMagic(magicKey);
            return new ResultValueDto<AuthDto>(new AuthDto() { Ticket = _srvAccess.GetToken() });
        }

        [AllowAnonymous]
        [HttpGet("{ticket}/ping")]
        public async Task<ResultValueDto<PingDto>> Ping(string ticket)
        {
            CheckAccess(ticket);
            return new ResultValueDto<PingDto>(new PingDto(){Status = "OK"});
        }

        [AllowAnonymous]
        [HttpPost("{ticket}/login")]
        public async Task<ResultValueDto<UserRDto>> Login(string ticket, [FromHeader] string userName, [FromHeader] string userPassword)
        {
            CheckAccess(ticket);
            return await _srvAccess.Login(userName, userPassword, LoginRoleAllowed);
        }


        public async Task<TResult> Call<TResult>(Func<Task<TResult>> block) where TResult : ResultDto, new()
        {
            var result = new TResult();
            try
            {
                return await block();
            }
            catch (Exception e)
            {
                ProcessException(result, e);
            }

            return result;
        }

        protected void ProcessException(ResultDto result, Exception exception)
        {
             if (exception is CodeException codeException)
                result.Errors = new[] { new ErrorMessageDto(codeException) };
             else 
                 result.Errors = new[] { new ErrorMessageDto(exception, 0001) };
        }
        protected void LogInfo(string message)
        {
            _logger.Log(LogLevel.Information, message);
        }

        protected void LogDebug(string message)
        {
            _logger.Log(LogLevel.Debug, message);
        }
        protected void LogTrace(string message)
        {
            _logger.Log(LogLevel.Trace, message);
        }
        protected void LogWarn(string message)
        {
            _logger.Log(LogLevel.Warning, message);
        }
        protected void LogWarn(string message, Exception exception)
        {
            _logger.Log(LogLevel.Warning, message);
        } 
        protected void LogWarn(Exception exception)
        {
            _logger.Log(LogLevel.Warning, exception.ToString());
        }
        protected void LogError(string message)
        {
            _logger.Log(LogLevel.Error, message);
        }
        protected void LogError(string message, Exception exception)
        {
            _logger.Log(LogLevel.Error, message);
        } 
        protected void LogError(Exception exception)
        {
            _logger.Log(LogLevel.Error, exception.ToString());
        }


        protected void CheckAccess(string token)
        {
            _srvAccess.CheckToken(token);
        }
    }
}
