using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User;
using Microsoft.AspNetCore.Authorization;
using DigitalData.UserManager.Application;
using DigitalData.UserManager.Application.DTOs.Auth;
using DigitalData.Core.Abstractions.Application;
using Microsoft.Extensions.Localization;
using DigitalData.Core.DTO;

namespace DigitalData.UserManager.API.Controllers
{
    [Route("api/[controller]")]
    public class AuthController : ControllerBase
    {
        private readonly IUserService _userService;
        private readonly IGroupOfUserService _gouService;
        private readonly IDirectorySearchService _dirSearchService;
        private readonly IStringLocalizer<Resource> _localizer;
        private readonly ILogger<AuthController> _logger;

        public AuthController(IUserService userService, IGroupOfUserService gouService, IDirectorySearchService directorySearchService, IStringLocalizer<Resource> localizer, ILogger<AuthController> logger)
        {
            _userService = userService;
            _gouService = gouService;
            _dirSearchService = directorySearchService;
            _localizer = localizer;
            _logger = logger;
        }

        [AllowAnonymous]
        [HttpGet("check")]
        public IActionResult CheckAuthentication()
        {
            try
            {
                return Ok(User.Identity?.IsAuthenticated ?? false);
            }
            catch(Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }

        [AllowAnonymous]
        [HttpPost("login")]
        public async Task<IActionResult> Login([FromBody] LogInDto login)
        {
            try
            {
                bool isValid = _dirSearchService.ValidateCredentials(login.Username, login.Password);

                if (!isValid)
                    return Unauthorized(Result.Fail().Message(_localizer[Key.UserNotFound]));

                var gouMsg = await _gouService.HasGroup(login.Username, "PM_USER", caseSensitive: false);
                if (!gouMsg.IsSuccess)
                    return Unauthorized(Result.Fail().Message(_localizer[Key.UnauthorizedUser]));

                //find the user
                var uRes = await _userService.ReadByUsernameAsync(login.Username);
                if (!uRes.IsSuccess || uRes.Data is null)
                {
                    return Unauthorized(uRes);
                }

                UserReadDto user = uRes.Data;

                // Create claims
                var claims = new List<Claim>
            {
                new (ClaimTypes.NameIdentifier, user.Id.ToString()),
                new (ClaimTypes.Name, user.Username),
                new (ClaimTypes.Surname, user.Name ?? ""),
                new (ClaimTypes.GivenName, user.Prename ?? ""),
                new (ClaimTypes.Email, user.Email ?? ""),
                new (ClaimTypes.Role, "PM_USER")
            };

                // Create claimsIdentity
                var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

                // Create authProperties
                var authProperties = new AuthenticationProperties
                {
                    IsPersistent = true,
                    AllowRefresh = true,
                    ExpiresUtc = DateTime.UtcNow.AddMinutes(60)
                };

                // Sign in
                await HttpContext.SignInAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(claimsIdentity),
                    authProperties);

                _dirSearchService.SetSearchRootCache(user.Username, login.Password);

                return Ok();
            }
            catch(Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }

        [Authorize]
        [HttpGet("user")]
        public async Task<IActionResult> GetUserWithClaims()
        {
            try
            {
                // Extract the username from the Name claim.
                string? username = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;

                if (string.IsNullOrEmpty(username))
                    return Unauthorized();

                return await _userService.ReadByUsernameAsync(username)
                    .ThenAsync(Ok, IActionResult (m, n) =>
                    {
                        _logger.LogNotice(n);
                        return NotFound(Result.Fail().Message(_localizer[Key.UserNotFound]));
                    });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }

        [Authorize]
        [HttpPost("logout")]
        public async Task<IActionResult> Logout()
        {
            try
            {
                await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
                return Ok();
            }
            catch(Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }
    }
}