using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using GZTW.Pecklist.Models;
using GZTW.Pecklist.Util;
using System.Linq;
using System;

//required to inject configuration in constructor
using Microsoft.Extensions.Configuration;

namespace GZTW.Pecklist.Controllers
{
    //Authentication controller
    public class AuthController : Controller
    {
        private readonly PecklistContext _context;
        private readonly IConfiguration _configuration;
        public AuthController(PecklistContext context, IConfiguration configuration)//these two are injected, see startup.cs
        {
             
            _context = context;
            //_configuration = configuration;
            //guard clause
            _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
        }

        //AUTHENTICATE CREDS 
        //RETURN JWT        

        [HttpPost("/authenticate")]
        public JsonResult PostCreds(string login, string password)
        {
            int nFailedAuthDelay = 10000;

            if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
            {
                //Make a failed pw wait
                System.Threading.Thread.Sleep(nFailedAuthDelay);
                return Json(new { msg = "authentication failed", error = 1 });
            }

            var user = _context.User.SingleOrDefault(m => m.Login == login);

            if (user == null)
            {
                //Make a failed pw wait
                System.Threading.Thread.Sleep(nFailedAuthDelay);
                return Json(new { msg = "authentication failed", error = 1 });
            }

            //TODO: do a test login from postman, login as john, then copy pw into db
            //string pwnewJohn=Hasher.hash(user.Salt,"XXX");
            //then login as Joyce and copy pw into db
            // string pwnewJoyce=Hasher.hash(user.Salt,"XXX");

            string hashed = Hasher.hash(user.Salt, password);
            if (hashed == user.Password)
            {
                //get teh secret from appsettings.json
                var secret = _configuration.GetSection("JWT").GetValue<string>("secret");
                byte[] secretKey = System.Text.Encoding.ASCII.GetBytes(secret);

                var iat = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
                //NOTE: pecklist is appropriate for a really long expiry
                var exp = new DateTimeOffset(DateTime.Now.AddDays(365)).ToUnixTimeSeconds();

                //Generate a download token and store it with the user account and return it for the client
                Guid g = Guid.NewGuid();
                string dlkey = Convert.ToBase64String(g.ToByteArray());
                dlkey = dlkey.Replace("=", "");
                dlkey = dlkey.Replace("+", "");

                user.DlKey = dlkey;
                user.DlKeyExp = exp;
                _context.User.Update(user);
                _context.SaveChanges();


                var payload = new Dictionary<string, object>()
                        {
                            { "iat", iat.ToString() },
                            { "exp", exp.ToString() },
                            { "iss", "GZTW_Pecklist" },
                            { "id", user.Id.ToString() }
                        };


                //NOTE: probably don't need Jose.JWT as am using Microsoft jwt stuff to validate routes so it should also be able to
                //issue tokens as well, but it looked cmplex and this works so unless need to remove in future keeping it.
                string token = Jose.JWT.Encode(payload, secretKey, Jose.JwsAlgorithm.HS256);
                //string jsonDecoded = Jose.JWT.Decode(token, secretKey);

                return Json(new
                {
                    ok = 1,
                    issued = iat,
                    expires = exp,
                    token = token,
                    dlkey = dlkey,
                    name = user.Name,
                    id = user.Id
                });
            }
            else
            {
                //Make a failed pw wait
                System.Threading.Thread.Sleep(nFailedAuthDelay);
                return Json(new { msg = "authentication failed", error = 1 });
            }
        }

        //------------------------------------------------------

    }//eoc
}//eons