WarehouseResourceImpl.java

package com.fulfilment.application.monolith.warehouses.adapters.restapi;

import com.fulfilment.application.monolith.exception.HttpStatus;
import com.fulfilment.application.monolith.warehouses.domain.ports.ArchiveWarehouseOperation;
import com.fulfilment.application.monolith.warehouses.domain.ports.CreateWarehouseOperation;
import com.fulfilment.application.monolith.warehouses.domain.ports.ReplaceWarehouseOperation;
import com.fulfilment.application.monolith.warehouses.domain.ports.WarehouseStore;
import com.warehouse.api.WarehouseResource;
import com.warehouse.api.beans.Warehouse;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.WebApplicationException;
import java.util.List;
import org.jboss.logging.Logger;

@RequestScoped
public class WarehouseResourceImpl implements WarehouseResource {

  // Alias: domain model uses fully qualified name throughout to avoid conflict with API bean
  private static final Logger LOGGER = Logger.getLogger(WarehouseResourceImpl.class.getName());

  @Inject WarehouseStore warehouseStore;

  @Inject CreateWarehouseOperation createWarehouseOperation;

  @Inject ArchiveWarehouseOperation archiveWarehouseOperation;

  @Inject ReplaceWarehouseOperation replaceWarehouseOperation;

  @Override
  public List<Warehouse> listAllWarehousesUnits() {
    LOGGER.debug("Listing all active warehouses");
    return warehouseStore.getAll().stream().map(this::toApiWarehouse).toList();
  }

  @Override
  @Transactional
  public Warehouse createANewWarehouseUnit(@NotNull Warehouse data) {
    validateForCreate(data);
    LOGGER.debugf("Request to create warehouse: %s", data.getBusinessUnitCode());

    com.fulfilment.application.monolith.warehouses.domain.models.Warehouse domain =
        toDomainWarehouse(data);

    createWarehouseOperation.create(domain);

    LOGGER.infof("Warehouse '%s' created", domain.businessUnitCode);
    return toApiWarehouse(domain);
  }

  @Override
  public Warehouse getAWarehouseUnitByID(String id) {
    LOGGER.debugf("Fetching warehouse by id: %s", id);

    com.fulfilment.application.monolith.warehouses.domain.models.Warehouse warehouse =
        warehouseStore.findActiveById(parseId(id));

    if (warehouse == null) {
      LOGGER.warnf("Warehouse with id '%s' not found", id);
      throw new NotFoundException("Warehouse with id '" + id + "' not found.");
    }

    return toApiWarehouse(warehouse);
  }

  @Override
  @Transactional
  public void archiveAWarehouseUnitByID(String id) {
    LOGGER.debugf("Request to archive warehouse with id: %s", id);

    com.fulfilment.application.monolith.warehouses.domain.models.Warehouse warehouse =
        warehouseStore.findActiveById(parseId(id));

    if (warehouse == null) {
      LOGGER.warnf("Warehouse with id '%s' not found for archiving", id);
      throw new NotFoundException("Warehouse with id '" + id + "' not found.");
    }

    archiveWarehouseOperation.archive(warehouse);
    LOGGER.infof("Warehouse with id '%s' archived", id);
  }

  @Override
  @Transactional
  public Warehouse replaceTheCurrentActiveWarehouse(
      String businessUnitCode, @NotNull Warehouse data) {
    validateForReplace(data);
    LOGGER.debugf("Request to replace warehouse: %s", businessUnitCode);

    // Set the businessUnitCode from the path — new warehouse inherits the same code
    com.fulfilment.application.monolith.warehouses.domain.models.Warehouse newWarehouse =
        toDomainWarehouse(data);
    newWarehouse.businessUnitCode = businessUnitCode;

    replaceWarehouseOperation.replace(newWarehouse);

    LOGGER.infof("Warehouse '%s' replaced successfully", businessUnitCode);
    return toApiWarehouse(newWarehouse);
  }

  // ─────────────────────────────────────────────────────────────
  // ID parsing helper
  // ─────────────────────────────────────────────────────────────

  /** Parses a String path parameter as a Long ID. Returns -1L if non-numeric (yields 404). */
  private Long parseId(String id) {
    try {
      return Long.parseLong(id);
    } catch (NumberFormatException e) {
      return -1L;
    }
  }

  // ─────────────────────────────────────────────────────────────
  // Validation helpers
  // ─────────────────────────────────────────────────────────────

  /**
   * Validates a create request — businessUnitCode must be present and well-formed in the body.
   */
  private void validateForCreate(Warehouse data) {
    if (data.getBusinessUnitCode() == null || data.getBusinessUnitCode().isBlank()) {
      throw new WebApplicationException(
          "Warehouse business unit code is required.", HttpStatus.UNPROCESSABLE_ENTITY);
    }
    if (!data.getBusinessUnitCode().matches("^[A-Z0-9][A-Z0-9.\\-_]{2,19}$")) {
      throw new WebApplicationException(
          "Warehouse business unit code has an invalid format (e.g. MWH.001).",
          HttpStatus.UNPROCESSABLE_ENTITY);
    }
    validateForReplace(data);
  }

  /**
   * Validates a replace request body — businessUnitCode is intentionally excluded
   * because it is supplied via the path parameter, not the body.
   */
  private void validateForReplace(Warehouse data) {
    if (data.getLocation() == null || data.getLocation().isBlank()) {
      throw new WebApplicationException(
          "Warehouse location is required.", HttpStatus.UNPROCESSABLE_ENTITY);
    }
    if (data.getCapacity() == null || data.getCapacity() < 0) {
      throw new WebApplicationException(
          "Warehouse capacity must be a non-negative number.", HttpStatus.UNPROCESSABLE_ENTITY);
    }
    if (data.getStock() == null || data.getStock() < 0) {
      throw new WebApplicationException(
          "Warehouse stock must be a non-negative number.", HttpStatus.UNPROCESSABLE_ENTITY);
    }
  }

  // ─────────────────────────────────────────────────────────────
  // Mapping helpers
  // ─────────────────────────────────────────────────────────────

  /** Maps domain Warehouse → API response Warehouse. */
  private Warehouse toApiWarehouse(
      com.fulfilment.application.monolith.warehouses.domain.models.Warehouse warehouse) {
    var response = new Warehouse();
    if (warehouse.id != null) {
      response.setId(String.valueOf(warehouse.id));
    }
    response.setBusinessUnitCode(warehouse.businessUnitCode);
    response.setLocation(warehouse.location);
    response.setCapacity(warehouse.capacity);
    response.setStock(warehouse.stock);
    return response;
  }

  /** Maps API request Warehouse → domain Warehouse. */
  private com.fulfilment.application.monolith.warehouses.domain.models.Warehouse toDomainWarehouse(
      Warehouse apiWarehouse) {
    var warehouse =
        new com.fulfilment.application.monolith.warehouses.domain.models.Warehouse();
    warehouse.businessUnitCode = apiWarehouse.getBusinessUnitCode();
    warehouse.location = apiWarehouse.getLocation();
    warehouse.capacity = apiWarehouse.getCapacity();
    warehouse.stock = apiWarehouse.getStock();
    return warehouse;
  }
}