package com.saas.admin.controller;

import com.saas.admin.dto.request.AssignRolesRequest;
import com.saas.admin.dto.response.UserWithRolesResponse;
import com.saas.admin.entity.Role;
import com.saas.admin.entity.User;
import com.saas.admin.service.impl.RBACServiceImpl;
import com.saas.shared.dto.common.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.util.HashSet;
import org.springframework.web.bind.annotation.*;

import java.util.Set;
import java.util.stream.Collectors;

/**
 * REST Controller for role-based access control (RBAC) management.
 * 
 * Path: /api/admin/users/{userId}/roles
 * Role: SYSTEM_ADMIN only
 * 
 * Endpoints:
 * - POST   /              Assign roles to user
 * - GET    /              Get user's roles
 * - DELETE /{roleId}      Remove role from user
 */
@RestController
@RequestMapping("/api/admin/users/{userId}/roles")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "User Roles", description = "Manage user role assignments")
@SecurityRequirement(name = "Bearer Authentication")
public class AdminUserRoleController {

    private final RBACServiceImpl rbacService;

    @PostMapping
    @Operation(summary = "Assign roles to user", description = "Assign one or more roles to a user")
    @ApiResponses(value = {
        @io.swagger.v3.oas.annotations.responses.ApiResponse(
            responseCode = "200",
            description = "Roles assigned successfully",
            content = @Content(schema = @Schema(implementation = ApiResponse.class))
        ),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Validation error"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "Unauthorized"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Forbidden"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "User or role not found")
    })
    public ResponseEntity<ApiResponse<UserWithRolesResponse>> assignRolesToUser(
            @Parameter(description = "User ID", example = "1")
            @PathVariable Long userId,
            @Valid @RequestBody AssignRolesRequest request) {
        log.info("Assigning {} roles to user: {}", request.getRoleIds().size(), userId);

        User updatedUser = rbacService.assignRolesToUser(userId, new HashSet<>(request.getRoleIds()));

        UserWithRolesResponse response = mapUserWithRoles(updatedUser);

        return ResponseEntity.ok()
            .body(ApiResponse.success(response, "Roles assigned successfully"));
    }

    @GetMapping
    @Operation(summary = "Get user's roles", description = "Retrieve all roles assigned to a user")
    @ApiResponses(value = {
        @io.swagger.v3.oas.annotations.responses.ApiResponse(
            responseCode = "200",
            description = "Roles retrieved successfully",
            content = @Content(schema = @Schema(implementation = ApiResponse.class))
        ),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "Unauthorized"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Forbidden"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "User not found")
    })
    public ResponseEntity<ApiResponse<UserWithRolesResponse>> getUserRoles(
            @Parameter(description = "User ID", example = "1")
            @PathVariable Long userId) {
        log.info("Fetching roles for user: {}", userId);

        Set<Role> roles = rbacService.getUserRoles(userId);

        // Create minimal response with roles only
        UserWithRolesResponse response = UserWithRolesResponse.builder()
            .id(userId)
            .roles(mapRoles(roles))
            .build();

        return ResponseEntity.ok()
            .body(ApiResponse.success(response, "User roles retrieved successfully"));
    }

    @DeleteMapping("/{roleId}")
    @Operation(summary = "Remove role from user", description = "Unassign a role from a user")
    @ApiResponses(value = {
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "204", description = "Role removed successfully"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "Unauthorized"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Forbidden"),
        @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "User or role not found")
    })
    public ResponseEntity<Void> removeRoleFromUser(
            @Parameter(description = "User ID", example = "1")
            @PathVariable Long userId,
            @Parameter(description = "Role ID", example = "1")
            @PathVariable Long roleId) {
        log.info("Removing role {} from user: {}", roleId, userId);

        rbacService.removeRoleFromUser(userId, roleId);

        return ResponseEntity.noContent().build();
    }

    /**
     * Helper method to map User entity with roles to response DTO.
     */
    private UserWithRolesResponse mapUserWithRoles(User user) {
        Set<Role> roles = user.getUserRoles().stream()
            .map(ur -> ur.getRole())
            .collect(Collectors.toSet());

        UserWithRolesResponse response = new UserWithRolesResponse();
        response.setId(user.getId());
        response.setEmail(user.getEmail());
        response.setFirstName(user.getFirstName());
        response.setLastName(user.getLastName());
        response.setIsActive("ACTIVE".equals(user.getStatus()));
        response.setRoles(mapRoles(roles));

        return response;
    }

    /**
     * Helper method to map Role entities to response DTOs.
     */
    private Set<UserWithRolesResponse.RoleResponse> mapRoles(Set<Role> roles) {
        return roles.stream()
            .map(role -> {
                UserWithRolesResponse.RoleResponse roleResponse = new UserWithRolesResponse.RoleResponse();
                roleResponse.setId(role.getId());
                roleResponse.setName(role.getName());
                roleResponse.setDescription(role.getDescription());
                roleResponse.setIsActive(role.getIsActive());
                return roleResponse;
            })
            .collect(Collectors.toSet());
    }
}
