ResponseEntity> updateStageAndStatus(@PathVariable Long requestId, @Valid @RequestBody StageUpdateDTO stageUpdate) {
        try {
            // Decrypt the payload 
            System.out.println("stageUpdate :: ::"+ stageUpdate);

            String decryptedData = CryptoUtil.decrypt(String.valueOf(stageUpdate)).toString();

            System.out.println("decryptedData"+decryptedData);

            // Check if the stageName is a number (index) and map it to the stage name
            String stageName = stageUpdate.getStageName();
            if (stageName != null && stageName.matches("\\d+")) {
                int stageIndex = Integer.parseInt(stageName);
                if (stageIndex < 0 || stageIndex >= StageOrder.getStageOrder().size()) {
                    throw new IllegalArgumentException("Invalid stage index: " + stageIndex);
                }
                // Map the index to the corresponding stage name
                stageUpdate.setStageName(StageOrder.getStageOrder().get(stageIndex));
            }
            // Validate stage order and process stage update in the service layer
            TrackingRequestDto updatedTracking = service.updateStageAndStatus(requestId, stageUpdate, StageOrder.getStageOrder());
            // Return success response
            return ResponseEntity.ok(ResponseUtil.success(updatedTracking, "Stage updated successfully"));
        } catch (IllegalArgumentException e) {
            // Return bad request with error response
            return ResponseEntity.badRequest().body(ResponseUtil.error(e.getMessage()));
        } catch (Exception e) {
            // Return internal server error with error response
            return ResponseEntity.internalServerError().body(ResponseUtil.error("Error updating stage: " + e.getMessage()));
        }
    }
/**
     * Decrypts the given encrypted data and converts it back to the specified type.
     *
     * @param encryptedData the Base64 encoded encrypted string
     * @param valueType the expected type to convert the decrypted JSON back into
     * @param  the type parameter
     * @return the decrypted object of type T
     * @throws Exception if decryption fails
     */
    public static  T decrypt(String encryptedData, Class valueType) throws Exception {
        try {
            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), ALGORITHM);
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
            byte[] decryptedBytes = cipher.doFinal(decodedBytes);
            String decryptedJson = new String(decryptedBytes, StandardCharsets.UTF_8);
            return objectMapper.readValue(decryptedJson, valueType);
        } catch (Exception e) {
            // Log error as needed
            throw new Exception("Decryption failed", e);
        }
    }

    /**
     * Decrypts the given encrypted data and returns it as a generic Object.
     *
     * @param encryptedData the Base64 encoded encrypted string
     * @return the decrypted object (typically a Map or List, depending on the JSON)
     * @throws Exception if decryption fails
     */
    public static Object decrypt(String encryptedData) throws Exception {
        return decrypt(encryptedData, Object.class);
    }
}
package com.infra_automation.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * Data Transfer Object for updating a stage.
 */
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class StageUpdateDTO {


    @NotBlank(message = "Hostname preference is required")
    @Size(max = 255, message = "Hostname preference must be 255 characters or less")
    @Pattern(regexp = "^[a-zA-Z0-9 ]*$", message = "HTML or script tags are not allowed")
    private String stageName;  // e.g., "HOD_Approval"


    @NotBlank(message = "Status is required")
    @Pattern(regexp = "^(completed|pending|rejected)$", message = "Invalid status not in this format completed|pending|rejected")
    @Pattern(regexp = "^[a-zA-Z0-9 ]*$", message = "HTML or script tags are not allowed")
    private String status;     // e.g., "Completed"


    @Pattern(regexp = "^[a-zA-Z0-9 ]*$", message = "HTML or script tags are not allowed")
    @Size(max = 100, message = "Done by must be 100 characters or less")
    private String doneBy;



    @Size(max = 255, message = "Remarks must be 255 characters or less")
    @Pattern(regexp = "^[a-zA-Z0-9 ]*$", message = "HTML or script tags are not allowed")
    private String remarks;    // e.g., "VM request raised by Admin"


    @Pattern(regexp = "^[a-zA-Z0-9 ]*$", message = "HTML or script tags are not allowed")
    private String action;

    @Size(max = 100, message = "Title must be 100 characters or less")
    @Pattern(regexp = "^[a-zA-Z0-9 ]*$", message = "HTML or script tags are not allowed")
    private String title;      // e.g., "Hod Approval"
}