using Knoks.Core.Data.Interfaces;
using Knoks.Core.Entities;
using Knoks.Core.Entities.Args;
using Knoks.Core.Logic.Interfaces;
using Knoks.Framework.Cryptography;
using Knoks.Framework.Security;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Knoks.Core.Logic.Managers
{
    public class OperatorManager : IOperatorManager
    {
        private readonly ILogger<OperatorManager> _logger;
        private readonly IOperatorDao _operatorDao;
        private readonly IPasswordStrength _passwordStrength;
        private readonly IPasswordProcess _passwordProcess;
        private readonly IDataProtectionProvider _dataProtectionProvider;

        public OperatorManager(
            ILogger<OperatorManager> logger,
            IOperatorDao operatorDao,
            IPasswordStrength passwordStrength,
            IPasswordProcess passwordProcess,
            IDataProtectionProvider dataProtectionProvider)
        {
            _logger = logger;
            _operatorDao = operatorDao;
            _passwordStrength = passwordStrength;
            _passwordProcess = passwordProcess;
            _dataProtectionProvider = dataProtectionProvider;
        }

        public async Task<Operator> CreateOperator(CreateOperatorArgs args)
        {
            if (await _passwordStrength.CheckStrength(args.OperatorPassword) < PasswordScore.Medium)
                throw new System.InvalidOperationException("Password is not enough strong.");

            args.OperatorPassword = _passwordProcess.Generate(args.OperatorPassword).ToFlatString();

            return await _operatorDao.CreateOperator(args);
        }

        public async Task<IEnumerable<Operator>> GetOperators(int? operatorId = null)
        {
            return await _operatorDao.GetOperators(operatorId);
        }

        public async Task<Operator> AuthenticateOperator(int apiConsumerId, string operatorName, string operatorPassword)
        {
            _logger.LogDebug($"AuthenticateOperator: {nameof(operatorName)}: '{operatorName}'");

            var obj = await _operatorDao.GetOperatorCredential(apiConsumerId, operatorName);
            if (obj == null) return null; //Operator is not exists

            var savedPasswordResult = new PasswordResult(obj.OperatorPassword);
            var userPasswordResult = _passwordProcess.Generate(operatorPassword, savedPasswordResult.Salt);

            return userPasswordResult.IsIdentical(savedPasswordResult) ? obj.Operator : null;
        }
    }
}
