code: package io.paradoxical.cassieq.admin.resources.api.v1;
  
  import com.google.inject.Inject;
  import io.paradoxical.cassieq.discoverable.auth.AccountAuth;
  import io.paradoxical.cassieq.dataAccess.interfaces.MonotonicRepository;
  import io.paradoxical.cassieq.dataAccess.interfaces.PointerRepository;
  import io.paradoxical.cassieq.resources.api.BaseQueueResource;
  import io.paradoxical.cassieq.factories.DataContextFactory;
  import io.paradoxical.cassieq.factories.MessageRepoFactory;
  import io.paradoxical.cassieq.factories.MonotonicRepoFactory;
  import io.paradoxical.cassieq.factories.QueueDataContext;
  import io.paradoxical.cassieq.factories.QueueRepositoryFactory;
  import io.paradoxical.cassieq.factories.ReaderFactory;
  import io.paradoxical.cassieq.model.InvisibilityMessagePointer;
  import io.paradoxical.cassieq.model.Message;
  import io.paradoxical.cassieq.model.MonotonicIndex;
  import io.paradoxical.cassieq.model.QueueDefinition;
  import io.paradoxical.cassieq.model.QueueName;
  import io.paradoxical.cassieq.model.ReaderBucketPointer;
  import io.paradoxical.cassieq.model.RepairBucketPointer;
  import io.paradoxical.cassieq.model.accounts.AccountName;
  import io.swagger.annotations.Api;
  import io.swagger.annotations.ApiOperation;
  import io.swagger.annotations.ApiResponse;
  import io.swagger.annotations.ApiResponses;
  import lombok.Getter;
  import org.joda.time.DateTime;
  
  import javax.ws.rs.DefaultValue;
  import javax.ws.rs.GET;
  import javax.ws.rs.Path;
  import javax.ws.rs.PathParam;
  import javax.ws.rs.Produces;
  import javax.ws.rs.QueryParam;
  import javax.ws.rs.core.MediaType;
  import javax.ws.rs.core.Response;
  import java.util.List;
  import java.util.Optional;
  
  import static java.util.stream.Collectors.toList;
  
  @Path("/api/v1/debug/accounts/{accountName}/queues")
  @Api(value = "/api/v1/debug/accounts/{accountName}/queues", description = "Queue diagnostic api", tags = { "cassieq-debug" })
  @Produces(MediaType.APPLICATION_JSON)
  public class QueueDebugResource extends BaseQueueResource {
  
      private final DataContextFactory dataContextFactory;
  
      @Inject
      public QueueDebugResource(
              ReaderFactory readerFactory,
              MessageRepoFactory messageRepoFactory,
              MonotonicRepoFactory monotonicRepoFactory,
              QueueRepositoryFactory queueRepository,
              DataContextFactory dataContextFactory,
              @PathParam("accountName") AccountName accountName) {
          super(readerFactory, messageRepoFactory, monotonicRepoFactory, queueRepository.forAccount(accountName), accountName);
          this.dataContextFactory = dataContextFactory;
      }
  
      @GET
      @ApiOperation(value = "Get all queues")
      @ApiResponses(value = {
              @ApiResponse(code = 200, message = "OK"),
              @ApiResponse(code = 500, message = "Server Error")
      })
      public Response getQueues() {
  
          final List<QueueDefinition> queues = getQueueRepository().getActiveQueues();
  
          return Response.ok(queues)
                         .build();
      }
  
      @GET
      @Path("/{queueName}/buckets/{bucketPointer}/messages")
      @ApiOperation(value = "Get bucket raw messages")
      @ApiResponses(value = {
              @ApiResponse(code = 200, message = "OK"),
              @ApiResponse(code = 404, message = "Queue doesn't exist"),
              @ApiResponse(code = 500, message = "Server Error")
      })
      public Response getBucketMessages(
              @PathParam("queueName") QueueName queueName,
              @PathParam("bucketPointer") Long bucketPointer,
              @QueryParam("onlyUnacked") @DefaultValue("false") Boolean onlyUnacked) {
  
          final QueueDefinition definition = lookupQueueDefinition(queueName);
  
          List<Message> messages = getMessageRepoFactory()
                  .forQueue(definition)
                  .getBucketContents(ReaderBucketPointer.valueOf(bucketPointer));
  
          if (onlyUnacked) {
              messages = messages.stream().filter(Message::isAcked).collect(toList());
          }
  
          return Response.ok(messages)
                         .build();
      }
  
      @GET
      @Path("/{queueName}/buckets/current/messages")
      @ApiOperation(value = "Get bucket raw messages")
      @ApiResponses(value = {
              @ApiResponse(code = 200, message = "OK"),
              @ApiResponse(code = 404, message = "Queue doesn't exist"),
              @ApiResponse(code = 500, message = "Server Error")
      })
      public Response getCurrentBucketMessages(
              @PathParam("queueName") QueueName queueName,
              @QueryParam("onlyUnacked") @DefaultValue("false") Boolean onlyUnacked) {
  
          final QueueDefinition definition = lookupQueueDefinition(queueName);
  
          final QueueDataContext dataContext = dataContextFactory.forQueue(definition);
  
          List<Message> messages =
                  getMessageRepoFactory()
                          .forQueue(definition)
                          .getBucketContents(dataContext.getPointerRepository().getReaderCurrentBucket());
  
          if (onlyUnacked) {
              messages = messages.stream().filter(Message::isAcked).collect(toList());
          }
  
          return Response.ok(messages)
                         .build();
      }
  
      @GET
      @Path("/{queueName}/buckets/{bucketPointer}/tombstone")
      @ApiOperation(value = "Get bucket sealed time")
      @ApiResponses(value = {
              @ApiResponse(code = 200, message = "OK"),
              @ApiResponse(code = 404, message = "Queue doesn't exist"),
              @ApiResponse(code = 500, message = "Server Error")
      })
      public Response isBucketSealed(
              @PathParam("queueName") QueueName queueName,
              @PathParam("bucketPointer") Long bucketPointer) {
  
          final QueueDefinition definition = lookupQueueDefinition(queueName);
  
          final Optional<DateTime> tombstoneTime =
                  getMessageRepoFactory()
                          .forQueue(definition)
                          .tombstoneExists(ReaderBucketPointer.valueOf(bucketPointer));
  
          return Response.ok(tombstoneTime)
                         .build();
      }
  
      @GET
      @Path("/{queueName}/messages/{messagePointer}")
      @ApiOperation(value = "Raw get message")
      @ApiResponses(value = {
              @ApiResponse(code = 200, message = "OK"),
              @ApiResponse(code = 404, message = "Queue doesn't exist"),
              @ApiResponse(code = 500, message = "Server Error")
      })
      public Response getMessage(
              @PathParam("queueName") QueueName queueName,
              @PathParam("messagePointer") Long messagePointer) {
  
          final QueueDefinition definition = lookupQueueDefinition(queueName);
  
          final Message message = getMessageRepoFactory().forQueue(definition)
                                                         .getMessage(MonotonicIndex.valueOf(messagePointer));
  
          return Response.ok(message)
                         .build();
      }
  
      @GET
      @Path("/{queueName}/monotons/current")
      @ApiOperation(value = "Get current monoton value")
      @ApiResponses(value = {
              @ApiResponse(code = 200, message = "OK"),
              @ApiResponse(code = 404, message = "Queue doesn't exist"),
              @ApiResponse(code = 500, message = "Server Error")
      })
      public Response getCurrentMonotonValue(
              @PathParam("queueName") QueueName queueName) {
  
          final QueueDefinition definition = lookupQueueDefinition(queueName);
  
          final MonotonicRepository monotonicRepository = getMonotonicRepoFactory().forQueue(definition.getId());
  
          return Response.ok(monotonicRepository.getCurrent())
                         .build();
      }
  
      @GET
      @Path("/{queueName}/pointers")
      @ApiOperation(value = "Get current pointer values")
      @ApiResponses(value = {
              @ApiResponse(code = 200, message = "OK"),
              @ApiResponse(code = 404, message = "Queue doesn't exist"),
              @ApiResponse(code = 500, message = "Server Error")
      })
      public Response getPointers(
              @PathParam("queueName") QueueName queueName) {
  
          final QueueDefinition definition = lookupQueueDefinition(queueName);
  
          final QueueDataContext dataContext = dataContextFactory.forQueue(definition);
  
          final PointerRepository pointerRepository = dataContext.getPointerRepository();
  
          final InvisibilityMessagePointer currentInvisPointer = pointerRepository.getCurrentInvisPointer();
          final ReaderBucketPointer readerCurrentBucket = pointerRepository.getReaderCurrentBucket();
          final RepairBucketPointer repairCurrentBucketPointer = pointerRepository.getRepairCurrentBucketPointer();
  
          return Response.ok(new Object() {
              @Getter
              private final InvisibilityMessagePointer currentInvisibilityPointer = currentInvisPointer;
  
              @Getter
              private final ReaderBucketPointer currentReaderBucket = readerCurrentBucket;
  
              @Getter
              private final RepairBucketPointer currentRepairBucket = repairCurrentBucketPointer;
          }).build();
      }
  }
  