package de.effigenix.domain.production;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;

import static org.assertj.core.api.Assertions.assertThat;

@DisplayName("Consumption Entity")
class ConsumptionTest {

    private ConsumptionDraft validDraft() {
        return new ConsumptionDraft("input-batch-1", "article-1", "10.5", "KILOGRAM");
    }

    @Nested
    @DisplayName("create()")
    class Create {

        @Test
        @DisplayName("should create consumption with valid draft")
        void should_CreateConsumption_When_ValidDraft() {
            var result = Consumption.create(validDraft());

            assertThat(result.isSuccess()).isTrue();
            var consumption = result.unsafeGetValue();
            assertThat(consumption.id()).isNotNull();
            assertThat(consumption.inputBatchId().value()).isEqualTo("input-batch-1");
            assertThat(consumption.articleId().value()).isEqualTo("article-1");
            assertThat(consumption.quantityUsed().amount()).isEqualByComparingTo(new BigDecimal("10.5"));
            assertThat(consumption.quantityUsed().uom().name()).isEqualTo("KILOGRAM");
            assertThat(consumption.consumedAt()).isNotNull();
        }

        @Test
        @DisplayName("should fail when inputBatchId is blank")
        void should_Fail_When_InputBatchIdBlank() {
            var draft = new ConsumptionDraft("", "article-1", "10", "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.ValidationFailure.class);
        }

        @Test
        @DisplayName("should fail when inputBatchId is null")
        void should_Fail_When_InputBatchIdNull() {
            var draft = new ConsumptionDraft(null, "article-1", "10", "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.ValidationFailure.class);
        }

        @Test
        @DisplayName("should fail when articleId is blank")
        void should_Fail_When_ArticleIdBlank() {
            var draft = new ConsumptionDraft("input-batch-1", "", "10", "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.ValidationFailure.class);
        }

        @Test
        @DisplayName("should fail when articleId is null")
        void should_Fail_When_ArticleIdNull() {
            var draft = new ConsumptionDraft("input-batch-1", null, "10", "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.ValidationFailure.class);
        }

        @Test
        @DisplayName("should fail when quantity is zero")
        void should_Fail_When_QuantityZero() {
            var draft = new ConsumptionDraft("input-batch-1", "article-1", "0", "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.InvalidConsumptionQuantity.class);
        }

        @Test
        @DisplayName("should fail when quantity is negative")
        void should_Fail_When_QuantityNegative() {
            var draft = new ConsumptionDraft("input-batch-1", "article-1", "-5", "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.InvalidConsumptionQuantity.class);
        }

        @Test
        @DisplayName("should fail when quantity is not a number")
        void should_Fail_When_QuantityNotANumber() {
            var draft = new ConsumptionDraft("input-batch-1", "article-1", "abc", "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.InvalidConsumptionQuantity.class);
        }

        @Test
        @DisplayName("should fail when quantity is null")
        void should_Fail_When_QuantityNull() {
            var draft = new ConsumptionDraft("input-batch-1", "article-1", null, "KILOGRAM");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.InvalidConsumptionQuantity.class);
        }

        @Test
        @DisplayName("should fail when unit is null")
        void should_Fail_When_UnitNull() {
            var draft = new ConsumptionDraft("input-batch-1", "article-1", "10", null);

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.InvalidConsumptionQuantity.class);
        }

        @Test
        @DisplayName("should fail when unit is invalid")
        void should_Fail_When_UnitInvalid() {
            var draft = new ConsumptionDraft("input-batch-1", "article-1", "10", "INVALID_UNIT");

            var result = Consumption.create(draft);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(BatchError.InvalidConsumptionQuantity.class);
        }
    }
}
