using Butter.Dtos.User;
using Butter.Types;
using Lactose.Mapper;
using Lactose.Models;
using Lactose.Repositories;
using Lactose.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace Lactose.Controllers {
    [ApiController]
    [Authorize]
    [Route("api/[controller]")]
    public class UserController(
        ILogger<UserController> logger,
        LactoseAuthService authService,
        IUserRepository userRepository,
        IPasswordHasher<User> passwordHasher
    ) : ControllerBase {
        [Authorize(Roles = "Admin")]
        [HttpPut]
        public ActionResult<UserInfoDto> Create([FromBody] UserCreateDto userCreateDto) {
            LactoseAuthenticatedUser? authenticatedUser = authService.GetUserData(User);

            if (authenticatedUser != null) {
                logger.LogWarning(@"Anonymous User hasn't enough permissions to create a User. it shouldn't be here");
            }

            if (authenticatedUser != null && authenticatedUser.AccessLevel != EAccessLevel.Admin) return Forbid();

            logger.LogDebug(
                $"""
                 Registering user:
                 Username: {userCreateDto.Username}
                 Email:    {userCreateDto.Email}
                 Access:   {userCreateDto.AccessLevel}
                 """
            );

            var user = new User {
                Username    = userCreateDto.Username,
                Email       = userCreateDto.Email,
                Password    = userCreateDto.Password,
                AccessLevel = userCreateDto.AccessLevel ?? EAccessLevel.User
            };

            if (userRepository.GetAll().Any(x => x.Email == user.Email)) return Conflict("Email already exists");
            if (userRepository.GetAll().Any(x => x.Username == user.Username)) return Conflict("Username already exists");

            userRepository.Insert(user);
            userRepository.Save();

            return Ok(user.Id);
        }


        [HttpGet("{id}")]
        public ActionResult<UserInfoDto> Get(Guid id) {
            LactoseAuthenticatedUser? authenticatedUser = authService.GetUserData(User);

            if (authenticatedUser == null) {
                logger.LogWarning(@"Anonymous User hasn't enough permissions to read User data. it shouldn't be here");
                return Unauthorized("Unauthorized");
            }

            //TODO: Lower level of access should fill less information of users
            logger.LogDebug($"Sending user with id {id}");

            User? user = userRepository.Find(id);

            if (user is null) return NotFound();

            return Ok(user.ToGetUsersDto());
        }

        [HttpGet]
        public ActionResult<List<UserInfoDto>> GetAll() {
            logger.LogDebug("Sending Users list");
            LactoseAuthenticatedUser? authenticatedUser = authService.GetUserData(User);

            if (authenticatedUser == null) {
                logger.LogWarning(@"Anonymous User hasn't enough permissions to create a User. it shouldn't be here");
                return Unauthorized("Unauthorized");
            }

            //TODO: Lower level of access should fill less information of users    
            //if (false) return Unauthorized();

            return Ok(userRepository.GetAll().Select(u => u.ToGetUsersDto()).ToList());
        }


        [HttpPost("update")]
        public ActionResult<UserInfoDto> Update([FromBody] UserUpdateDto dto) {
            LactoseAuthenticatedUser? authenticatedUser = authService.GetUserData(User);

            if (authenticatedUser == null) {
                logger.LogWarning(@"Anonymous User hasn't enough permissions to create a User. it shouldn't be here");
                return Unauthorized("Unauthorized");
            }

            logger.LogDebug(
                $"""
                 Editing user:
                 Id:       {dto.Id}
                 Username: {dto.Username}
                 Email:    {dto.Email}
                 IsBanned: {dto.IsBanned}
                 IsDeleted:{dto.IsDeleted}
                 Access:   {dto.AccessLevel}
                 """
            );

            if (authenticatedUser.AccessLevel != EAccessLevel.Admin && authenticatedUser.Id != dto.Id) return Forbid();

            User? user = userRepository.Find(dto.Id);
            if (user == null) return NotFound();

            if (dto.Username != null) { user.Username = dto.Username; }

            if (dto.Email != null) { user.Email = dto.Email; }

            if (dto.Password != null) { user.Password = passwordHasher.HashPassword(user, dto.Password); }

            if (dto.IsDeleted != null) { user.DeletedAt = dto.IsDeleted.Value ? DateTime.Now : null; }

            if (authenticatedUser.AccessLevel == EAccessLevel.Admin) {
                if (dto.IsBanned != null) {
                    if (user.BannedAt != null) user.BannedAt = dto.IsBanned.Value ? DateTime.Now : null;
                }

                if (dto.AccessLevel != null) { user.AccessLevel = dto.AccessLevel.Value; }
            }

            userRepository.Save();
            return Ok(user.ToGetUsersDto());
        }

        [Authorize(Roles = "Admin")]
        [HttpDelete("{id}")]
        public ActionResult<Guid> Delete([FromRoute] Guid id) {
            LactoseAuthenticatedUser? authenticatedUser = authService.GetUserData(User);

            if (authenticatedUser == null) {
                logger.LogWarning(@"Anonymous hasn't enough permissions to delete a User. it shouldn't be here");
                return Unauthorized("Unauthorized");
            }

            logger.LogDebug($"Deleting user: {id}");

            User? user = userRepository.Find(id);

            if (user == null) return NotFound();

            userRepository.Delete(user);

            //TODO: eventually remove/hide all of the user content if required
            userRepository.Save();
            return Ok(id);
        }
    }
}
