using adas_core.Application.Exceptions;
using adas_core.Application.Services.Interfaces;
using adas_core.Authentication.Attributes;
using adas_core.Authentication.Interfaces;
using adas_core.Authentication.Models;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.MongoModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;

namespace adas_core.Controllers;

[ApiController]
[Route("users")]
public class UserController(
    IUserService userService,
    IAuthorityService authorityService,
    Lazy<IPermissionService> permissionService)
    : ControllerBase
{
    [AllowAnonymous]
    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginInfo loginInfo)
    {
        var token = await userService.Login(loginInfo.Username, loginInfo.Password);
        var authorities = await authorityService.GetUserAuthorities(token.User.Id);
        token.User.Authorization = authorities;
        token.User.Password = "";
        return Ok(UciResponse<TokenResult>.FromSuccess(token));
    }
    [AllowAnonymous]
    [HttpGet("token")]
    public async Task<IActionResult> LoginWithBearer()
    {
        var user = (User?)HttpContext.Items["User"] ??
                   throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedUserNotRegistered);
        var newJwtToken = await userService.GenerateJwt(user);
        var authorities = await authorityService.GetUserAuthorities(newJwtToken.User.Id);
        newJwtToken.User.Authorization = authorities;
        return Ok(UciResponse<TokenResult>.FromSuccess(newJwtToken));
    }

    [AllowAnonymous]
    [HttpGet("login")]
    public async Task<IActionResult> LoginWithAccessToken([FromQuery] string token)
    {
        var accessToken = await userService.LoginWithAccessToken(token);
        accessToken.AccessToken = token;
        userService.ValidateToken(token, "user", out var tokenRead);
        accessToken.AccessTokenExpiryTime = tokenRead.ValidTo;
        return Ok(UciResponse<TokenResult>.FromSuccess(accessToken));
    }

    [AllowAnonymous]
    [HttpPost("refresh-token")]
    public async Task<IActionResult> RefreshToken([FromBody] string refreshToken)
    {
        var token = await userService.RefreshToken(refreshToken);
        return Ok(UciResponse<TokenResult>.FromSuccess(token));
    }

    [AllowAnonymous]
    [HttpGet("login-cas")]
    public async Task<IActionResult> LoginWithCas([FromQuery] string service, [FromQuery] string ticket)
    {
        try
        {
            var user = await userService.GetUserByCasTicket(service, ticket);
            if (user != null)
            {
                var newJwtToken = await userService.GenerateJwt(user);
                return Ok(UciResponse<TokenResult>.FromSuccess(newJwtToken));
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);
        }

        return NotFound();
    }

    [HttpGet("me")]
    public IActionResult Me()
    {
        var user = (User?)HttpContext.Items["User"] ??
                   throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedUserNotRegistered);
        return Ok(UciResponse<User>.FromSuccess(user));
    }

    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAll()
    {
        var users = await userService.GetAll();
        return Ok(UciResponse<List<User>>.FromSuccess(users));
    }


    [HttpGet("authorities")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllAuthorities()
    {
        var authorities = await authorityService.GetAllAuthorities();
        return Ok(UciResponse<List<Authorization>>.FromSuccess(authorities));
    }

    [HttpPost("paginated")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> PaginatedUsers([FromBody] PaginationFilter config)
    {
        if (config == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var result = await userService.GetPaginatedUsers(config);
        return Ok(result);
    }

    #region UciPanel
    [AllowAnonymous]
    [HttpPost("login/adm-panel")]
    public async Task<IActionResult> LoginPanel([FromBody] LoginInfo loginInfo)
    {
        var token = await userService.Login(loginInfo.Username, loginInfo.Password);
        var authorities = await authorityService.GetUserAuthorities(token.User.Id);
        token.User.Authorization = authorities;
        token.User.Password = "";
        var result = new TokenResultPanel
        {
            TokenResult = token,
            Permissions = permissionService.Value.GetPermissionsForPanel(token.User)
        };
        return Ok(UciResponse<TokenResultPanel>.FromSuccess(result));
    }
    [AllowAnonymous]
    [HttpGet("token/adm-panel")]
    public async Task<IActionResult> LoginWithBearerPanel()
    {
        var user = (User?)HttpContext.Items["User"] ??
                   throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedUserNotRegistered);
        var newJwtToken = await userService.GenerateJwt(user);
        var authorities = await authorityService.GetUserAuthorities(newJwtToken.User.Id);
        newJwtToken.User.Authorization = authorities;
        var result = new TokenResultPanel
        {
            TokenResult = newJwtToken,
            Permissions = permissionService.Value.GetPermissionsForPanel(newJwtToken.User)
        };
        return Ok(UciResponse<TokenResultPanel>.FromSuccess(result));
    }
    [AllowAnonymous]
    [HttpGet("login/adm-panel")]
    public async Task<IActionResult> LoginWithAccessTokenPanel([FromQuery] string token)
    {
        var accessToken = await userService.LoginWithAccessToken(token);
        var result = new TokenResultPanel
        {
            TokenResult = accessToken,
            Permissions = permissionService.Value.GetPermissionsForPanel(accessToken.User)
        };
        return Ok(UciResponse<TokenResultPanel>.FromSuccess(result));
    }

    [HttpPost("refresh-token/adm-panel")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> RefreshTokenPanel([FromBody] string refreshToken)
    {
        var token = await userService.RefreshToken(refreshToken);
        var result = new TokenResultPanel
        {
            TokenResult = token,
            Permissions = permissionService.Value.GetPermissionsForPanel(token.User)
        };
        return Ok(UciResponse<TokenResultPanel>.FromSuccess(result));
    }

    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> NewUser(User u)
    {
        var newUser = await userService.CreateNewUserByRequest(u);
        return Ok(newUser);
    }

    [HttpPost("authorities")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> NewUserWithAuthorities([FromBody] CreateUserWithAuthDto createUserWithAuthDto)
    {
        var user = await userService.CreateNewUserWithAuthorities(createUserWithAuthDto);
        return Ok(user);
    }

    [HttpDelete("{userId}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteUser(string userId)
    {
        var isParsed = ObjectId.TryParse(userId, out var userIdParsed);
        if (!isParsed) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var isDeleted = await userService.DeleteUser(userIdParsed);

        if (isDeleted)
        {
            await authorityService.DeleteAuthoritiesForUser(userIdParsed);
            return Ok();
        }

        throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
    }

    [HttpPut("{updatePass}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> EditUser(User u, bool updatePass)
    {
        _ = await userService.UpdateUsersByRequest(u, updatePass) ??
            throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
        return Ok();
    }

    [HttpPut("{updatePass}/authorities")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> EditUserWithAuthorities(UpdateUserWithAuthDto updateUserWithAuthDto,
        bool updatePass)
    {
        await userService.UpdateUserWithAuthorities(updateUserWithAuthDto, updatePass);
        return Ok();
    }

    [HttpPost("authority")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> NewUserAuthority(Authorization authorization)
    {
        var auth = await userService.CreateNewAuthority(authorization);
        return Ok(auth);
    }

    [HttpDelete("authority/{authorityId}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteUserAuthority(string authorityId)
    {
        await userService.DeleteAuthority(authorityId);
        return Ok();
    }

    [HttpPut("authorities")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteUserAuthorities([FromBody] List<string> auths)
    {
        foreach (var auth in auths) await userService.DeleteAuthority(auth);
        return Ok();
    }

    [HttpPost("{id}/password")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeleteUserAuthorities(string id, UpdatePasswordDto updatePasswordDto)
    {
        if (!ObjectId.TryParse(id, out var userIdParsed))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var result = await userService.UpdateUserPassword(userIdParsed, updatePasswordDto.OldPassword,
            updatePasswordDto.NewPassword);

        return Ok(result);
    }

    [HttpPut("authority")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> EditUserAuthority(Authorization authorization)
    {
        await userService.UpdateAuthority(authorization);
        return Ok();
    }

    [HttpGet("{userId}/authority")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAuthorityByUserId(string userId)
    {
        if (!ObjectId.TryParse(userId, out var userIdParsed))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var authorities = await authorityService.GetUserAuthorities(userIdParsed);

        return Ok(UciResponse<List<Authorization>>.FromSuccess(authorities));
    }

    #endregion
}