using Microsoft.AspNetCore.Mvc;
using Auth.Services.DatabaseService;
using System.Web.Http;
using Auth.Entities;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Auth.Services;
using Auth.DTO;

namespace Auth.Controllers {
    [ApiController]
    [Route("api/v1/mauth/")]
    public class MAuthController : MistoxControllerBase {

        public MAuthController(DatabaseService db) : base(db) { }

        static Dictionary<string, JWTMemCache> LoginSessions = new Dictionary<string, JWTMemCache>();

        // Login and return a ticket to retreive your JWT
        [HttpPost("login")]
        public async Task<ActionResult> Authenticate([FromBody] LoginRequest request) {
            try {
                Account? test = await _databaseService.GetAccount(request.UserName.ToLower());
                if (test != null) {
                    if (test.EmailVerified == true) {
                        if (test.FailedPasswordLock) {
                            if (test.CurrentPasswordAttempts >= test.PasswordAttempts) {
                                return BadRequest("Too many failed password attempts. Please reset your password");
                            }
                        }
                        if (BCrypt.Net.BCrypt.Verify(request.Password, test.PasswordHash)) {
                            test.CurrentPasswordAttempts = 0;
                            await _databaseService.SetAccount(test);

                            string Ticket = Guid.NewGuid().ToString().Replace("-", "");
                            LoginSessions[Ticket] = new JWTMemCache {
                                JWT = AuthJWT.GenereateJWTToken(test, request.StayLoggedIn),
                                ExpiresAt = DateTime.UtcNow.AddMinutes(2)
                            };

                            return Ok(Ticket);
                        } else {
                            test.CurrentPasswordAttempts += 1;
                            await _databaseService.SetAccount(test);
                            return BadRequest("Wrong Password");
                        }
                    } else {
                        return BadRequest("You need to verify your email before you can sign in");
                    }
                }
                return BadRequest("Account Not Found");
            } catch (Exception ex) {
                Console.WriteLine("Login Error: " + ex.Message);
                return BadRequest("An internal server error has occured");
            }
        }

        [HttpPost("token")]
        public ActionResult Token([FromForm] JWTRequest request) {
            try {
                if (LoginSessions.ContainsKey(request.Ticket)) {
                    JWTMemCache JWTObj = LoginSessions[request.Ticket];
                    if (JWTObj.ExpiresAt < DateTime.UtcNow) {
                        string JWT = JWTObj.JWT;
                        LoginSessions.Remove(request.Ticket);
                        return Ok(JWT);
                    } else {
                        LoginSessions.Remove(request.Ticket);
                        return BadRequest("The session ticket has already expired");
                    }
                }
                return BadRequest("The session ticket cannot be found");
            } catch (Exception ex) {
                Console.WriteLine("Token Error: " + ex.Message);
                return BadRequest("An internal server error has occured");
            }
        }

        // Renews an old JWT before it expires
        [HttpPost("renew")]
        public IActionResult Session([FromBody] JWTRenewRequest request) {
            try {
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                ClaimsPrincipal claimsPrincipal = handler.ValidateToken(request.JWT, AuthJWT.TokenParameters, out var validatedToken);
                JwtSecurityToken jwt = (JwtSecurityToken)validatedToken;
                if (jwt != null) {
                    if (jwt.ValidTo - DateTime.UtcNow < TimeSpan.FromDays(1)) {
                        var newJwt = AuthJWT.RenewJWTToken(claimsPrincipal);
                        return Ok(newJwt);
                    }
                    return BadRequest("Not ready to renew");
                }
                return BadRequest("Malformed Token");
            } catch (SecurityTokenException ex) {
                return BadRequest("Token invalid: " + ex.Message);
            }
        }

    }
}
