package de.effigenix.application.inventory;

import de.effigenix.domain.inventory.*;
import de.effigenix.domain.masterdata.article.ArticleId;
import de.effigenix.shared.common.RepositoryError;
import de.effigenix.shared.common.Result;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
@DisplayName("GetStock Use Case")
class GetStockTest {

    @Mock private StockRepository stockRepository;

    private GetStock getStock;
    private Stock existingStock;

    @BeforeEach
    void setUp() {
        getStock = new GetStock(stockRepository);

        existingStock = Stock.reconstitute(
                StockId.of("stock-1"),
                ArticleId.of("article-1"),
                StorageLocationId.of("location-1"),
                null, null, List.of(), List.of()
        );
    }

    @Test
    @DisplayName("should return stock when found")
    void shouldReturnStockWhenFound() {
        when(stockRepository.findById(StockId.of("stock-1")))
                .thenReturn(Result.success(Optional.of(existingStock)));

        var result = getStock.execute("stock-1");

        assertThat(result.isSuccess()).isTrue();
        assertThat(result.unsafeGetValue().id().value()).isEqualTo("stock-1");
    }

    @Test
    @DisplayName("should fail with StockNotFound when stock does not exist")
    void shouldFailWhenStockNotFound() {
        when(stockRepository.findById(StockId.of("stock-1")))
                .thenReturn(Result.success(Optional.empty()));

        var result = getStock.execute("stock-1");

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

    @Test
    @DisplayName("should fail with RepositoryFailure when repository fails")
    void shouldFailWhenRepositoryFails() {
        when(stockRepository.findById(StockId.of("stock-1")))
                .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

        var result = getStock.execute("stock-1");

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

    @Test
    @DisplayName("should fail with StockNotFound when id is null")
    void shouldFailWhenIdIsNull() {
        var result = getStock.execute(null);

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

    @Test
    @DisplayName("should fail with StockNotFound when id is blank")
    void shouldFailWhenIdIsBlank() {
        var result = getStock.execute("  ");

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