package de.effigenix.application.inventory;

import de.effigenix.application.inventory.command.RecordStockMovementCommand;
import de.effigenix.domain.inventory.InventoryAction;
import de.effigenix.domain.inventory.StockMovement;
import de.effigenix.domain.inventory.StockMovementDraft;
import de.effigenix.domain.inventory.StockMovementError;
import de.effigenix.domain.inventory.StockMovementRepository;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.persistence.UnitOfWork;
import de.effigenix.shared.security.ActorId;
import de.effigenix.shared.security.AuthorizationPort;

public class RecordStockMovement {

    private final StockMovementRepository stockMovementRepository;
    private final AuthorizationPort authPort;
    private final UnitOfWork unitOfWork;

    public RecordStockMovement(StockMovementRepository stockMovementRepository, AuthorizationPort authPort, UnitOfWork unitOfWork) {
        this.stockMovementRepository = stockMovementRepository;
        this.authPort = authPort;
        this.unitOfWork = unitOfWork;
    }

    public Result<StockMovementError, StockMovement> execute(RecordStockMovementCommand cmd, ActorId performedBy) {
        if (!authPort.can(performedBy, InventoryAction.STOCK_MOVEMENT_WRITE)) {
            return Result.failure(new StockMovementError.Unauthorized("Not authorized to record stock movements"));
        }

        var draft = new StockMovementDraft(
                cmd.stockId(), cmd.articleId(), cmd.stockBatchId(),
                cmd.batchId(), cmd.batchType(),
                cmd.movementType(), cmd.direction(),
                cmd.quantityAmount(), cmd.quantityUnit(),
                cmd.reason(), cmd.referenceDocumentId(),
                cmd.performedBy()
        );

        StockMovement movement;
        switch (StockMovement.record(draft)) {
            case Result.Failure(var err) -> { return Result.failure(err); }
            case Result.Success(var val) -> movement = val;
        }

        return unitOfWork.executeAtomically(() -> {
            switch (stockMovementRepository.save(movement)) {
                case Result.Failure(var err) ->
                        { return Result.failure(new StockMovementError.RepositoryFailure(err.message())); }
                case Result.Success(var ignored) -> { }
            }
            return Result.success(movement);
        });
    }
}
