package de.effigenix.domain.inventory;

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

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

@DisplayName("BatchReference")
class BatchReferenceTest {

    @Nested
    @DisplayName("of()")
    class Of {

        @Test
        @DisplayName("should create with PRODUCED type")
        void shouldCreateWithProducedType() {
            var result = BatchReference.of("BATCH-001", "PRODUCED");

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().batchId()).isEqualTo("BATCH-001");
            assertThat(result.unsafeGetValue().batchType()).isEqualTo(BatchType.PRODUCED);
        }

        @Test
        @DisplayName("should create with PURCHASED type")
        void shouldCreateWithPurchasedType() {
            var result = BatchReference.of("PO-123", "PURCHASED");

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().batchId()).isEqualTo("PO-123");
            assertThat(result.unsafeGetValue().batchType()).isEqualTo(BatchType.PURCHASED);
        }

        @Test
        @DisplayName("should fail when batchId is null")
        void shouldFailWhenBatchIdNull() {
            var result = BatchReference.of(null, "PRODUCED");

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidBatchReference.class);
        }

        @Test
        @DisplayName("should fail when batchId is blank")
        void shouldFailWhenBatchIdBlank() {
            var result = BatchReference.of("  ", "PRODUCED");

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidBatchReference.class);
        }

        @Test
        @DisplayName("should fail when batchType is null")
        void shouldFailWhenBatchTypeNull() {
            var result = BatchReference.of("BATCH-001", null);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidBatchReference.class);
        }

        @Test
        @DisplayName("should fail when batchType is invalid")
        void shouldFailWhenBatchTypeInvalid() {
            var result = BatchReference.of("BATCH-001", "INVALID");

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidBatchReference.class);
            assertThat(result.unsafeGetError().message()).contains("INVALID");
        }

        @Test
        @DisplayName("should fail when batchType is empty string")
        void shouldFailWhenBatchTypeEmpty() {
            var result = BatchReference.of("BATCH-001", "");

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(StockError.InvalidBatchReference.class);
        }
    }

    @Nested
    @DisplayName("equals / hashCode")
    class Equality {

        @Test
        @DisplayName("should be equal with same batchId and batchType")
        void shouldBeEqualWithSameValues() {
            var ref1 = new BatchReference("BATCH-001", BatchType.PRODUCED);
            var ref2 = new BatchReference("BATCH-001", BatchType.PRODUCED);

            assertThat(ref1).isEqualTo(ref2);
            assertThat(ref1.hashCode()).isEqualTo(ref2.hashCode());
        }

        @Test
        @DisplayName("should not be equal with different batchType")
        void shouldNotBeEqualWithDifferentType() {
            var ref1 = new BatchReference("BATCH-001", BatchType.PRODUCED);
            var ref2 = new BatchReference("BATCH-001", BatchType.PURCHASED);

            assertThat(ref1).isNotEqualTo(ref2);
        }

        @Test
        @DisplayName("should not be equal with different batchId")
        void shouldNotBeEqualWithDifferentId() {
            var ref1 = new BatchReference("BATCH-001", BatchType.PRODUCED);
            var ref2 = new BatchReference("BATCH-002", BatchType.PRODUCED);

            assertThat(ref1).isNotEqualTo(ref2);
        }
    }
}
