using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OmsorgCore.Api.Contracts;
using OmsorgCore.Api.Security;
using OmsorgCore.Application.Services;
using OmsorgCore.Domain.Enums;

namespace OmsorgCore.Api.Controllers;

[ApiController]
[Authorize]
[Route("api/roles")]
public class RolesController : ControllerBase
{
    private readonly IRoleService _roleService;

    public RolesController(IRoleService roleService)
    {
        _roleService = roleService;
    }

    [HttpGet]
    [RequirePermission(ModuleType.UserManagement, PermissionAction.View)]
    public async Task<ActionResult<IReadOnlyList<RoleResponse>>> GetAll(CancellationToken cancellationToken)
    {
        var roles = await _roleService.GetAllAsync(cancellationToken);
        return Ok(roles.Select(r => new RoleResponse(r.Id, r.Name)).ToList());
    }

    [HttpPost]
    [RequirePermission(ModuleType.UserManagement, PermissionAction.Create)]
    public async Task<ActionResult<RoleResponse>> Create(CreateRoleRequest request, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(request.Name) || request.Name.Length > 100)
        {
            return BadRequest("Name ist erforderlich und darf maximal 100 Zeichen lang sein.");
        }

        var result = await _roleService.CreateAsync(request.Name, cancellationToken);
        if (!result.Success)
        {
            return result.FailureReason switch
            {
                CreateRoleFailureReason.NameTaken => Conflict("Diese Rolle existiert bereits."),
                _ => BadRequest("Name ist erforderlich.")
            };
        }

        return CreatedAtAction(nameof(GetAll), new RoleResponse(result.Role!.Id, result.Role.Name));
    }

    [HttpGet("{id:guid}")]
    [RequirePermission(ModuleType.UserManagement, PermissionAction.View)]
    public async Task<ActionResult<RolePermissionsResponse>> GetById(Guid id, CancellationToken cancellationToken)
    {
        var role = await _roleService.GetByIdWithPermissionsAsync(id, cancellationToken);
        if (role is null)
        {
            return NotFound("Rolle nicht gefunden.");
        }

        var permissions = role.RolePermissions
            .Select(rp => new PermissionDto(rp.Module, rp.Action, rp.Scope))
            .ToList();

        return Ok(new RolePermissionsResponse(role.Id, role.Name, permissions));
    }

    [HttpPut("{id:guid}/permissions")]
    [RequirePermission(ModuleType.UserManagement, PermissionAction.Edit)]
    public async Task<IActionResult> UpdatePermissions(Guid id, UpdateRolePermissionsRequest request, CancellationToken cancellationToken)
    {
        var parsed = request.Permissions.Select(dto => (dto.Module, dto.Action, dto.Scope)).ToList();

        var result = await _roleService.UpdatePermissionsAsync(id, parsed, cancellationToken);
        if (!result.Success)
        {
            return result.FailureReason switch
            {
                UpdateRolePermissionsFailureReason.RoleNotFound => NotFound("Rolle nicht gefunden."),
                _ => BadRequest()
            };
        }

        return NoContent();
    }

    [HttpDelete("{id:guid}")]
    [RequirePermission(ModuleType.UserManagement, PermissionAction.Delete)]
    public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
    {
        var result = await _roleService.DeleteAsync(id, cancellationToken);
        if (!result.Success)
        {
            return result.FailureReason switch
            {
                DeleteRoleFailureReason.RoleNotFound => NotFound("Rolle nicht gefunden."),
                DeleteRoleFailureReason.InUse => Conflict($"Diese Rolle ist noch {result.AssignedUserCount} Benutzer(n) zugewiesen und kann nicht gelöscht werden."),
                _ => BadRequest()
            };
        }

        return NoContent();
    }
}
