package de.effigenix.domain.production;

import de.effigenix.shared.common.Quantity;
import de.effigenix.shared.common.QuantityError;
import de.effigenix.shared.common.Result;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * Value Object representing production yield as a percentage (1–200).
 *
 * <p>Used to calculate required input quantities based on expected output.
 * A yield of 80% means that 80% of input becomes output, so to produce
 * 100 kg output you need 125 kg input (100 / 0.80).
 *
 * <p>Invariant: value must be between 1 and 200 (inclusive)
 */
public record YieldPercentage(int value) {

    public YieldPercentage {
        if (value < 1 || value > 200) {
            throw new IllegalArgumentException(
                    "Yield percentage must be between 1 and 200, was: " + value);
        }
    }

    /**
     * Creates a YieldPercentage, returning a Result.
     */
    public static Result<QuantityError, YieldPercentage> of(int value) {
        if (value < 1 || value > 200) {
            return Result.failure(new QuantityError.InvalidYieldPercentage(value));
        }
        return Result.success(new YieldPercentage(value));
    }

    /**
     * Calculates the required input quantity to achieve the desired output quantity
     * at this yield percentage.
     *
     * <p>Formula: requiredInput = desiredOutput / (yieldPercentage / 100)
     * <p>Example: YieldPercentage(80).calculateRequiredInput(100 kg) = 125 kg
     */
    public Quantity calculateRequiredInput(Quantity desiredOutput) {
        BigDecimal factor = new BigDecimal(100)
                .divide(new BigDecimal(value), 10, RoundingMode.HALF_UP);
        BigDecimal requiredAmount = desiredOutput.amount()
                .multiply(factor)
                .setScale(6, RoundingMode.HALF_UP);

        BigDecimal requiredSecondary = desiredOutput.secondaryAmount() != null
                ? desiredOutput.secondaryAmount().multiply(factor).setScale(6, RoundingMode.HALF_UP)
                : null;

        return Quantity.reconstitute(requiredAmount, desiredOutput.uom(),
                requiredSecondary, desiredOutput.secondaryUom());
    }

    /**
     * Returns the yield as a decimal factor (e.g., 80 → 0.80).
     */
    public BigDecimal asFactor() {
        return new BigDecimal(value)
                .divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
    }
}
