using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using IamService.API.Application.Common;
using IamService.API.Application.Commands.Roles;
using IamService.API.Application.Queries.Roles;
using RolesQueries = IamService.API.Application.Queries.Roles;
using RolesCommands = IamService.API.Application.Commands.Roles;

namespace IamService.API.Controllers;

/// <summary>
/// EN: Roles management controller.
/// VI: Controller quản lý roles.
/// </summary>
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/roles")]
[Authorize(AuthenticationSchemes = "Bearer")]
[Authorize(Policy = "RequireAdmin")]
[SwaggerTag("Role management endpoints - requires Admin role")]
public class RolesController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly ILogger<RolesController> _logger;

    public RolesController(
        IMediator mediator,
        ILogger<RolesController> logger)
    {
        _mediator = mediator;
        _logger = logger;
    }

    /// <summary>
    /// EN: Get all roles with pagination.
    /// VI: Lấy tất cả roles với phân trang.
    /// </summary>
    /// <param name="pageNumber">Page number (1-based)</param>
    /// <param name="pageSize">Number of items per page</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Paginated list of roles</returns>
    [HttpGet]
    [SwaggerOperation(
        Summary = "Get all roles",
        Description = "Retrieves a paginated list of all roles. Requires authentication.",
        OperationId = "GetRoles")]
    [SwaggerResponse(StatusCodes.Status200OK, "Successfully retrieved roles", typeof(ApiResponse<IEnumerable<RoleResponse>>))]
    [SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
    [ProducesResponseType(typeof(ApiResponse<IEnumerable<RoleResponse>>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> GetRoles(
        [FromQuery, SwaggerParameter("Page number (1-based)", Required = false)] int pageNumber = 1,
        [FromQuery, SwaggerParameter("Number of items per page", Required = false)] int pageSize = 10,
        CancellationToken cancellationToken = default)
    {
        var query = new GetRolesQuery(pageNumber, pageSize);
        var result = await _mediator.Send(query, cancellationToken);

        return Ok(new ApiResponse<IEnumerable<RoleResponse>>
        {
            Success = true,
            Data = result.Roles.Select(r => new RoleResponse
            {
                Id = r.Id,
                Name = r.Name,
                Description = r.Description,
                IsSystemRole = r.IsSystemRole,
                CreatedAt = r.CreatedAt,
                UserCount = r.UserCount,
                Permissions = r.Permissions?.ToList() ?? new()
            }),
            Pagination = new PaginationInfo
            {
                PageNumber = result.PageNumber,
                PageSize = result.PageSize,
                TotalCount = result.TotalCount
            }
        });
    }

    /// <summary>
    /// EN: Get role by ID.
    /// VI: Lấy role theo ID.
    /// </summary>
    /// <param name="id">Role ID</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Role information</returns>
    [HttpGet("{id:guid}")]
    [SwaggerOperation(
        Summary = "Get role by ID",
        Description = "Retrieves a specific role by its unique identifier.",
        OperationId = "GetRoleById")]
    [SwaggerResponse(StatusCodes.Status200OK, "Successfully retrieved role", typeof(ApiResponse<RoleResponse>))]
    [SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
    [SwaggerResponse(StatusCodes.Status404NotFound, "Role not found")]
    [ProducesResponseType(typeof(ApiResponse<RoleResponse>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetRoleById(
        [FromRoute, SwaggerParameter("Role ID", Required = true)] Guid id,
        CancellationToken cancellationToken = default)
    {
        var query = new GetRoleByIdQuery(id);
        var result = await _mediator.Send(query, cancellationToken);

        if (result == null)
        {
            return NotFound(ApiResponse<RoleResponse>.Fail("ROLE_NOT_FOUND", $"Role with ID {id} not found."));
        }

        return Ok(ApiResponse<RoleResponse>.Ok(new RoleResponse
        {
            Id = result.Id,
            Name = result.Name,
            Description = result.Description,
            IsSystemRole = result.IsSystemRole,
            CreatedAt = result.CreatedAt
        }));
    }

    /// <summary>
    /// EN: Create a new role.
    /// VI: Tạo role mới.
    /// </summary>
    /// <param name="request">Role creation data</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Created role</returns>
    [HttpPost]
    [SwaggerOperation(
        Summary = "Create role",
        Description = "Creates a new role with the specified name and description.",
        OperationId = "CreateRole")]
    [SwaggerResponse(StatusCodes.Status201Created, "Role created successfully", typeof(ApiResponse<RoleResponse>))]
    [SwaggerResponse(StatusCodes.Status400BadRequest, "Invalid request data")]
    [SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
    [SwaggerResponse(StatusCodes.Status409Conflict, "Role already exists")]
    [ProducesResponseType(typeof(ApiResponse<RoleResponse>), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> CreateRole(
        [FromBody, SwaggerRequestBody("Role creation data", Required = true)] CreateRoleRequest request,
        CancellationToken cancellationToken = default)
    {
        var command = new CreateRoleCommand(request.Name, request.Description);

        try
        {
            var result = await _mediator.Send(command, cancellationToken);

            var response = new RoleResponse
            {
                Id = result.Id,
                Name = result.Name,
                Description = result.Description,
                IsSystemRole = false,
                CreatedAt = result.CreatedAt
            };

            return CreatedAtAction(nameof(GetRoleById), new { id = result.Id }, ApiResponse<RoleResponse>.Ok(response));
        }
        catch (Exception ex) when (ex.Message.Contains("already exists"))
        {
            return Conflict(ApiResponse<RoleResponse>.Fail("ROLE_EXISTS", ex.Message));
        }
    }

    /// <summary>
    /// EN: Update role information.
    /// VI: Cập nhật thông tin role.
    /// </summary>
    /// <param name="id">Role ID to update</param>
    /// <param name="request">Update data</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Updated role information</returns>
    [HttpPut("{id:guid}")]
    [SwaggerOperation(
        Summary = "Update role",
        Description = "Updates a role's name and description. System roles cannot be updated.",
        OperationId = "UpdateRole")]
    [SwaggerResponse(StatusCodes.Status200OK, "Role updated successfully", typeof(ApiResponse<RoleResponse>))]
    [SwaggerResponse(StatusCodes.Status400BadRequest, "Invalid request data or cannot update system role")]
    [SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
    [SwaggerResponse(StatusCodes.Status404NotFound, "Role not found")]
    [ProducesResponseType(typeof(ApiResponse<RoleResponse>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> UpdateRole(
        [FromRoute, SwaggerParameter("Role ID to update", Required = true)] Guid id,
        [FromBody, SwaggerRequestBody("Role update data", Required = true)] UpdateRoleRequest request,
        CancellationToken cancellationToken = default)
    {
        var command = new UpdateRoleCommand(id, request.Name, request.Description);

        try
        {
            var result = await _mediator.Send(command, cancellationToken);

            return Ok(ApiResponse<RoleResponse>.Ok(new RoleResponse
            {
                Id = result.Id,
                Name = result.Name,
                Description = result.Description
            }));
        }
        catch (Exception ex) when (ex.Message.Contains("not found"))
        {
            return NotFound(ApiResponse<RoleResponse>.Fail("ROLE_NOT_FOUND", ex.Message));
        }
        catch (Exception ex) when (ex.Message.Contains("system"))
        {
            return BadRequest(ApiResponse<RoleResponse>.Fail("SYSTEM_ROLE", ex.Message));
        }
    }

    /// <summary>
    /// EN: Delete a role.
    /// VI: Xóa role.
    /// </summary>
    /// <param name="id">Role ID to delete</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Deletion result</returns>
    [HttpDelete("{id:guid}")]
    [SwaggerOperation(
        Summary = "Delete role",
        Description = "Deletes a role. System roles cannot be deleted.",
        OperationId = "DeleteRole")]
    [SwaggerResponse(StatusCodes.Status200OK, "Role deleted successfully")]
    [SwaggerResponse(StatusCodes.Status400BadRequest, "Cannot delete system role")]
    [SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
    [SwaggerResponse(StatusCodes.Status404NotFound, "Role not found")]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeleteRole(
        [FromRoute, SwaggerParameter("Role ID to delete", Required = true)] Guid id,
        CancellationToken cancellationToken = default)
    {
        var command = new DeleteRoleCommand(id);

        try
        {
            await _mediator.Send(command, cancellationToken);
            return Ok(ApiResponse<object>.Ok(new { Message = "Role deleted successfully." }));
        }
        catch (Exception ex) when (ex.Message.Contains("not found"))
        {
            return NotFound(ApiResponse<object>.Fail("ROLE_NOT_FOUND", ex.Message));
        }
        catch (Exception ex) when (ex.Message.Contains("system"))
        {
            return BadRequest(ApiResponse<object>.Fail("SYSTEM_ROLE", ex.Message));
        }
    }

    // ═══ PERMISSION ENDPOINTS ═══

    /// <summary>
    /// EN: Get permissions for a role (from role_claims table).
    /// VI: Lấy permissions cho một role (từ bảng role_claims).
    /// </summary>
    [HttpGet("{id:guid}/permissions")]
    [SwaggerOperation(Summary = "Get role permissions", OperationId = "GetRolePermissions")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetRolePermissions(Guid id, CancellationToken cancellationToken = default)
    {
        var result = await _mediator.Send(new GetRolePermissionsQuery(id), cancellationToken);
        if (result == null)
            return NotFound(ApiResponse<object>.Fail("ROLE_NOT_FOUND", $"Role with ID {id} not found."));
        return Ok(ApiResponse<object>.Ok(result));
    }

    /// <summary>
    /// EN: Update permissions for a role (replaces all permission claims).
    /// VI: Cập nhật permissions cho một role (thay thế toàn bộ permission claims).
    /// </summary>
    [HttpPut("{id:guid}/permissions")]
    [SwaggerOperation(Summary = "Update role permissions", OperationId = "UpdateRolePermissions")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> UpdateRolePermissions(
        Guid id, [FromBody] UpdateRolePermissionsRequest request, CancellationToken cancellationToken = default)
    {
        try
        {
            await _mediator.Send(new UpdateRolePermissionsCommand(id, request.Permissions), cancellationToken);
            var updated = await _mediator.Send(new GetRolePermissionsQuery(id), cancellationToken);
            return Ok(ApiResponse<object>.Ok(updated!));
        }
        catch (Exception ex) when (ex.Message.Contains("not found"))
        {
            return NotFound(ApiResponse<object>.Fail("ROLE_NOT_FOUND", ex.Message));
        }
        catch (Exception ex) when (ex.Message.Contains("system") || ex.Message.Contains("Invalid"))
        {
            return BadRequest(ApiResponse<object>.Fail("INVALID_REQUEST", ex.Message));
        }
    }

    // ═══ USER ROLE ASSIGNMENT ═══

    /// <summary>
    /// EN: Assign a role to a user.
    /// VI: Gán role cho user.
    /// </summary>
    /// <param name="userId">User ID</param>
    /// <param name="request">Role assignment data</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Assignment result</returns>
    [HttpPost("/api/v{version:apiVersion}/users/{userId:guid}/roles")]
    [SwaggerOperation(
        Summary = "Assign role to user",
        Description = "Assigns a role to a specific user.",
        OperationId = "AssignRoleToUser")]
    [SwaggerResponse(StatusCodes.Status200OK, "Role assigned successfully")]
    [SwaggerResponse(StatusCodes.Status400BadRequest, "Invalid request or user already has role")]
    [SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
    [SwaggerResponse(StatusCodes.Status404NotFound, "User or role not found")]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> AssignRoleToUser(
        [FromRoute, SwaggerParameter("User ID", Required = true)] Guid userId,
        [FromBody, SwaggerRequestBody("Role assignment data", Required = true)] AssignRoleRequest request,
        CancellationToken cancellationToken = default)
    {
        var command = new AssignRoleToUserCommand(userId, request.RoleName);

        try
        {
            await _mediator.Send(command, cancellationToken);
            return Ok(ApiResponse<object>.Ok(new { Message = $"Role '{request.RoleName}' assigned to user successfully." }));
        }
        catch (Exception ex) when (ex.Message.Contains("not found"))
        {
            return NotFound(ApiResponse<object>.Fail("NOT_FOUND", ex.Message));
        }
        catch (Exception ex) when (ex.Message.Contains("already has"))
        {
            return BadRequest(ApiResponse<object>.Fail("ROLE_ALREADY_ASSIGNED", ex.Message));
        }
    }

    /// <summary>
    /// EN: Remove a role from a user.
    /// VI: Xóa role khỏi user.
    /// </summary>
    /// <param name="userId">User ID</param>
    /// <param name="roleName">Role name to remove</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Removal result</returns>
    [HttpDelete("/api/v{version:apiVersion}/users/{userId:guid}/roles/{roleName}")]
    [SwaggerOperation(
        Summary = "Remove role from user",
        Description = "Removes a role from a specific user.",
        OperationId = "RemoveRoleFromUser")]
    [SwaggerResponse(StatusCodes.Status200OK, "Role removed successfully")]
    [SwaggerResponse(StatusCodes.Status400BadRequest, "User does not have this role")]
    [SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
    [SwaggerResponse(StatusCodes.Status404NotFound, "User not found")]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> RemoveRoleFromUser(
        [FromRoute, SwaggerParameter("User ID", Required = true)] Guid userId,
        [FromRoute, SwaggerParameter("Role name to remove", Required = true)] string roleName,
        CancellationToken cancellationToken = default)
    {
        var command = new RemoveRoleFromUserCommand(userId, roleName);

        try
        {
            await _mediator.Send(command, cancellationToken);
            return Ok(ApiResponse<object>.Ok(new { Message = $"Role '{roleName}' removed from user successfully." }));
        }
        catch (Exception ex) when (ex.Message.Contains("not found"))
        {
            return NotFound(ApiResponse<object>.Fail("NOT_FOUND", ex.Message));
        }
        catch (Exception ex) when (ex.Message.Contains("does not have"))
        {
            return BadRequest(ApiResponse<object>.Fail("ROLE_NOT_ASSIGNED", ex.Message));
        }
    }
}

#region Request/Response Models

/// <summary>
/// EN: Request body for creating role.
/// VI: Request body để tạo role.
/// </summary>
public class CreateRoleRequest
{
    /// <summary>
    /// EN: Role name.
    /// VI: Tên role.
    /// </summary>
    /// <example>Admin</example>
    public string Name { get; set; } = string.Empty;

    /// <summary>
    /// EN: Role description.
    /// VI: Mô tả role.
    /// </summary>
    /// <example>Administrator with full access</example>
    public string? Description { get; set; }
}

/// <summary>
/// EN: Request body for updating role.
/// VI: Request body để cập nhật role.
/// </summary>
public class UpdateRoleRequest
{
    /// <summary>
    /// EN: New role name.
    /// VI: Tên role mới.
    /// </summary>
    /// <example>SuperAdmin</example>
    public string Name { get; set; } = string.Empty;

    /// <summary>
    /// EN: New role description.
    /// VI: Mô tả role mới.
    /// </summary>
    /// <example>Super administrator with all permissions</example>
    public string? Description { get; set; }
}

/// <summary>
/// EN: Request body for assigning role to user.
/// VI: Request body để gán role cho user.
/// </summary>
public class AssignRoleRequest
{
    /// <summary>
    /// EN: Role name to assign.
    /// VI: Tên role cần gán.
    /// </summary>
    /// <example>Admin</example>
    public string RoleName { get; set; } = string.Empty;
}

/// <summary>
/// EN: Role response model.
/// VI: Model response cho role.
/// </summary>
public class RoleResponse
{
    /// <summary>
    /// EN: Role ID.
    /// VI: ID của role.
    /// </summary>
    public Guid Id { get; set; }

    /// <summary>
    /// EN: Role name.
    /// VI: Tên role.
    /// </summary>
    public string Name { get; set; } = string.Empty;

    /// <summary>
    /// EN: Role description.
    /// VI: Mô tả role.
    /// </summary>
    public string? Description { get; set; }

    /// <summary>
    /// EN: Whether this is a system role.
    /// VI: Role có phải là system role không.
    /// </summary>
    public bool IsSystemRole { get; set; }

    /// <summary>
    /// EN: Creation timestamp.
    /// VI: Thời gian tạo.
    /// </summary>
    public DateTime CreatedAt { get; set; }

    /// <summary>
    /// EN: Number of users assigned this role.
    /// VI: Số lượng người dùng được gán role này.
    /// </summary>
    public int UserCount { get; set; }

    /// <summary>
    /// EN: Permissions assigned to this role (from role_claims).
    /// VI: Permissions được gán cho role này (từ role_claims).
    /// </summary>
    public List<string> Permissions { get; set; } = new();
}

/// <summary>
/// EN: Request body for updating role permissions.
/// VI: Request body để cập nhật permissions cho role.
/// </summary>
public class UpdateRolePermissionsRequest
{
    /// <summary>
    /// EN: List of permission names to assign.
    /// VI: Danh sách tên permissions cần gán.
    /// </summary>
    public List<string> Permissions { get; set; } = new();
}

#endregion
