package de.effigenix.infrastructure.usermanagement.web.controller;

import de.effigenix.application.usermanagement.dto.RoleDTO;
import de.effigenix.shared.common.RepositoryError;
import de.effigenix.domain.usermanagement.Role;
import de.effigenix.domain.usermanagement.RoleRepository;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.security.ActorId;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.stream.Collectors;

/**
 * REST Controller for Role Management endpoints.
 *
 * Endpoints:
 * - GET /api/roles - List all roles (ADMIN only)
 *
 * Security:
 * - All endpoints require authentication (JWT token)
 * - ADMIN-only endpoints check for USER_MANAGEMENT permission
 *
 * Roles are reference data - typically loaded from seed data (Liquibase).
 * This controller provides read-only access to roles for user assignment.
 *
 * Infrastructure Layer → REST API
 */
@RestController
@RequestMapping("/api/roles")
@SecurityRequirement(name = "Bearer Authentication")
@Tag(name = "Role Management", description = "Role management endpoints (ADMIN only)")
public class RoleController {

    private static final Logger logger = LoggerFactory.getLogger(RoleController.class);

    private final RoleRepository roleRepository;

    public RoleController(RoleRepository roleRepository) {
        this.roleRepository = roleRepository;
    }

    /**
     * List all roles endpoint.
     *
     * Lists all available roles in the system.
     * Used for role assignment dropdowns in UI.
     *
     * Requires ADMIN permission (USER_MANAGEMENT).
     *
     * GET /api/roles
     * Authorization: Bearer <access-token>
     *
     * Response (200 OK):
     * [
     *   {
     *     "id": "role-uuid",
     *     "name": "ADMIN",
     *     "permissions": ["USER_MANAGEMENT", "INVENTORY_MANAGEMENT", ...],
     *     "description": "System administrator with full access"
     *   },
     *   {
     *     "id": "role-uuid-2",
     *     "name": "MANAGER",
     *     "permissions": ["INVENTORY_READ", "INVENTORY_WRITE", ...],
     *     "description": "Branch manager with inventory management"
     *   }
     * ]
     *
     * @param authentication Current authentication
     * @return List of role DTOs
     */
    @GetMapping
    @PreAuthorize("hasAuthority('ROLE_READ')")
    @Operation(
            summary = "List all roles (ADMIN only)",
            description = "Get a list of all available roles in the system. Requires USER_MANAGEMENT permission."
    )
    @ApiResponses({
            @ApiResponse(
                    responseCode = "200",
                    description = "Roles retrieved successfully",
                    content = @Content(schema = @Schema(implementation = RoleDTO.class))
            ),
            @ApiResponse(responseCode = "403", description = "Missing USER_MANAGEMENT permission"),
            @ApiResponse(responseCode = "401", description = "Authentication required")
    })
    public ResponseEntity<List<RoleDTO>> listRoles(Authentication authentication) {
        ActorId actorId = extractActorId(authentication);
        logger.info("Listing roles by actor: {}", actorId.value());

        var result = roleRepository.findAll();
        if (result.isFailure()) {
            throw new RoleDomainErrorException(result.unsafeGetError());
        }

        List<RoleDTO> roles = result.unsafeGetValue().stream()
                .map(RoleDTO::from)
                .collect(Collectors.toList());
        logger.info("Found {} roles", roles.size());
        return ResponseEntity.ok(roles);
    }

    // ==================== Helper Methods ====================

    private ActorId extractActorId(Authentication authentication) {
        if (authentication == null || authentication.getName() == null) {
            throw new IllegalStateException("No authentication found in SecurityContext");
        }
        return ActorId.of(authentication.getName());
    }

    public static class RoleDomainErrorException extends RuntimeException {
        private final RepositoryError error;

        public RoleDomainErrorException(RepositoryError error) {
            super(error.message());
            this.error = error;
        }

        public RepositoryError getError() {
            return error;
        }
    }
}
