package de.effigenix.application.masterdata;

import de.effigenix.application.masterdata.supplier.*;
import de.effigenix.application.masterdata.supplier.command.*;
import de.effigenix.domain.masterdata.supplier.*;
import de.effigenix.shared.common.ContactInfo;
import de.effigenix.shared.common.RepositoryError;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.persistence.UnitOfWork;
import de.effigenix.shared.security.ActorId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
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.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("Supplier Use Cases")
class SupplierUseCaseTest {

    @Mock private SupplierRepository supplierRepository;
    @Mock private UnitOfWork unitOfWork;

    private final ActorId performedBy = ActorId.of("admin-user");

    // ==================== Helpers ====================

    private static Supplier existingSupplier(String id) {
        return Supplier.reconstitute(
                SupplierId.of(id),
                new SupplierName("Test Supplier"),
                null,
                new ContactInfo("+49123456789", null, null),
                null,
                List.of(),
                null,
                SupplierStatus.ACTIVE,
                OffsetDateTime.now(ZoneOffset.UTC),
                OffsetDateTime.now(ZoneOffset.UTC)
        );
    }

    private static Supplier existingSupplierWithCertificate(String id, QualityCertificate certificate) {
        return Supplier.reconstitute(
                SupplierId.of(id),
                new SupplierName("Test Supplier"),
                null,
                new ContactInfo("+49123456789", null, null),
                null,
                List.of(certificate),
                null,
                SupplierStatus.ACTIVE,
                OffsetDateTime.now(ZoneOffset.UTC),
                OffsetDateTime.now(ZoneOffset.UTC)
        );
    }

    private static Supplier inactiveSupplier(String id) {
        return Supplier.reconstitute(
                SupplierId.of(id),
                new SupplierName("Inactive Supplier"),
                null,
                new ContactInfo("+49123456789", null, null),
                null,
                List.of(),
                null,
                SupplierStatus.INACTIVE,
                OffsetDateTime.now(ZoneOffset.UTC),
                OffsetDateTime.now(ZoneOffset.UTC)
        );
    }

    private static CreateSupplierCommand validCreateCommand() {
        return new CreateSupplierCommand(
                "Acme Supplies", "+49123456789", "info@acme.de", "Max Mustermann",
                null, null, null, null, null,
                null, null
        );
    }

    private static CreateSupplierCommand validCreateCommandWithAddress() {
        return new CreateSupplierCommand(
                "Acme Supplies", "+49123456789", "info@acme.de", "Max Mustermann",
                "Hauptstr.", "1", "12345", "Berlin", "DE",
                30, "30 Tage netto"
        );
    }

    // ==================== CreateSupplier ====================

    @Nested
    @DisplayName("CreateSupplier")
    class CreateSupplierTest {

        private CreateSupplier createSupplier;

        @BeforeEach
        void setUp() {
            lenient().when(unitOfWork.executeAtomically(any()))
                    .thenAnswer(inv -> ((java.util.function.Supplier<?>) inv.getArgument(0)).get());
            createSupplier = new CreateSupplier(supplierRepository, unitOfWork);
        }

        @Test
        @DisplayName("should create supplier with valid minimal data")
        void shouldCreateSupplierWithValidData() {
            when(supplierRepository.existsByName(any())).thenReturn(Result.success(false));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var result = createSupplier.execute(validCreateCommand(), performedBy);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().name().value()).isEqualTo("Acme Supplies");
            assertThat(result.unsafeGetValue().status()).isEqualTo(SupplierStatus.ACTIVE);
            verify(supplierRepository).save(any(Supplier.class));
        }

        @Test
        @DisplayName("should create supplier with full data including address and payment terms")
        void shouldCreateSupplierWithFullData() {
            when(supplierRepository.existsByName(any())).thenReturn(Result.success(false));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var result = createSupplier.execute(validCreateCommandWithAddress(), performedBy);

            assertThat(result.isSuccess()).isTrue();
            var supplier = result.unsafeGetValue();
            assertThat(supplier.address()).isNotNull();
            assertThat(supplier.paymentTerms()).isNotNull();
        }

        @Test
        @DisplayName("should fail when supplier name already exists")
        void shouldFailWhenNameAlreadyExists() {
            when(supplierRepository.existsByName(any())).thenReturn(Result.success(true));

            var result = createSupplier.execute(validCreateCommand(), performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNameAlreadyExists.class);
            verify(supplierRepository, never()).save(any());
        }

        @Test
        @DisplayName("should fail with ValidationFailure when name is blank")
        void shouldFailWhenNameIsBlank() {
            var cmd = new CreateSupplierCommand(
                    "", "+49123456789", null, null,
                    null, null, null, null, null, null, null
            );

            var result = createSupplier.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.ValidationFailure.class);
            verify(supplierRepository, never()).existsByName(any());
            verify(supplierRepository, never()).save(any());
        }

        @Test
        @DisplayName("should fail with ValidationFailure when email format is invalid")
        void shouldFailWhenEmailFormatIsInvalid() {
            var cmd = new CreateSupplierCommand(
                    "Acme Supplies", "+49123456789", "invalid-email", null,
                    null, null, null, null, null, null, null
            );

            var result = createSupplier.execute(cmd, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when existsByName fails")
        void shouldFailWhenExistsByNameFails() {
            when(supplierRepository.existsByName(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var result = createSupplier.execute(validCreateCommand(), performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when save fails")
        void shouldFailWhenSaveFails() {
            when(supplierRepository.existsByName(any())).thenReturn(Result.success(false));
            when(supplierRepository.save(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));

            var result = createSupplier.execute(validCreateCommand(), performedBy);

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

    // ==================== UpdateSupplier ====================

    @Nested
    @DisplayName("UpdateSupplier")
    class UpdateSupplierTest {

        private UpdateSupplier updateSupplier;

        @BeforeEach
        void setUp() {
            lenient().when(unitOfWork.executeAtomically(any()))
                    .thenAnswer(inv -> ((java.util.function.Supplier<?>) inv.getArgument(0)).get());
            updateSupplier = new UpdateSupplier(supplierRepository, unitOfWork);
        }

        @Test
        @DisplayName("should update supplier name")
        void shouldUpdateSupplierName() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var cmd = new UpdateSupplierCommand(
                    supplierId, "New Name", null, null, null,
                    null, null, null, null, null, null, null
            );

            var result = updateSupplier.execute(cmd, performedBy);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().name().value()).isEqualTo("New Name");
        }

        @Test
        @DisplayName("should fail with SupplierNotFound when supplier does not exist")
        void shouldFailWhenSupplierNotFound() {
            when(supplierRepository.findById(SupplierId.of("nonexistent")))
                    .thenReturn(Result.success(Optional.empty()));

            var cmd = new UpdateSupplierCommand(
                    "nonexistent", "New Name", null, null, null,
                    null, null, null, null, null, null, null
            );

            var result = updateSupplier.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNotFound.class);
            verify(supplierRepository, never()).save(any());
        }

        @Test
        @DisplayName("should fail with ValidationFailure when new name is blank")
        void shouldFailWhenNewNameIsBlank() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));

            var cmd = new UpdateSupplierCommand(
                    supplierId, "", null, null, null,
                    null, null, null, null, null, null, null
            );

            var result = updateSupplier.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.ValidationFailure.class);
            verify(supplierRepository, never()).save(any());
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when findById fails")
        void shouldFailWhenFindByIdFails() {
            when(supplierRepository.findById(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var cmd = new UpdateSupplierCommand(
                    "supplier-1", "New Name", null, null, null,
                    null, null, null, null, null, null, null
            );

            var result = updateSupplier.execute(cmd, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when save fails")
        void shouldFailWhenSaveFails() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));
            when(supplierRepository.save(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));

            var cmd = new UpdateSupplierCommand(
                    supplierId, "New Name", null, null, null,
                    null, null, null, null, null, null, null
            );

            var result = updateSupplier.execute(cmd, performedBy);

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

    // ==================== GetSupplier ====================

    @Nested
    @DisplayName("GetSupplier")
    class GetSupplierTest {

        private GetSupplier getSupplier;

        @BeforeEach
        void setUp() {
            getSupplier = new GetSupplier(supplierRepository);
        }

        @Test
        @DisplayName("should return supplier when found")
        void shouldReturnSupplierWhenFound() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.of(existingSupplier("supplier-1"))));

            var result = getSupplier.execute(supplierId);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().id()).isEqualTo(supplierId);
        }

        @Test
        @DisplayName("should fail with SupplierNotFound when not found")
        void shouldFailWhenNotFound() {
            var supplierId = SupplierId.of("nonexistent");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.empty()));

            var result = getSupplier.execute(supplierId);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNotFound.class);
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when repository fails")
        void shouldFailWhenRepositoryFails() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var result = getSupplier.execute(supplierId);

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

    // ==================== ListSuppliers ====================

    @Nested
    @DisplayName("ListSuppliers")
    class ListSuppliersTest {

        private ListSuppliers listSuppliers;

        @BeforeEach
        void setUp() {
            listSuppliers = new ListSuppliers(supplierRepository);
        }

        @Test
        @DisplayName("should return all suppliers")
        void shouldReturnAllSuppliers() {
            var suppliers = List.of(existingSupplier("s-1"), existingSupplier("s-2"));
            when(supplierRepository.findAll()).thenReturn(Result.success(suppliers));

            var result = listSuppliers.execute();

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue()).hasSize(2);
        }

        @Test
        @DisplayName("should return empty list when no suppliers exist")
        void shouldReturnEmptyList() {
            when(supplierRepository.findAll()).thenReturn(Result.success(List.of()));

            var result = listSuppliers.execute();

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue()).isEmpty();
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when findAll fails")
        void shouldFailWhenFindAllFails() {
            when(supplierRepository.findAll())
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var result = listSuppliers.execute();

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

        @Test
        @DisplayName("should return suppliers filtered by status")
        void shouldReturnSuppliersByStatus() {
            var activeSuppliers = List.of(existingSupplier("s-1"));
            when(supplierRepository.findByStatus(SupplierStatus.ACTIVE))
                    .thenReturn(Result.success(activeSuppliers));

            var result = listSuppliers.executeByStatus(SupplierStatus.ACTIVE);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue()).hasSize(1);
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when findByStatus fails")
        void shouldFailWhenFindByStatusFails() {
            when(supplierRepository.findByStatus(SupplierStatus.ACTIVE))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var result = listSuppliers.executeByStatus(SupplierStatus.ACTIVE);

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

    // ==================== ActivateSupplier ====================

    @Nested
    @DisplayName("ActivateSupplier")
    class ActivateSupplierTest {

        private ActivateSupplier activateSupplier;

        @BeforeEach
        void setUp() {
            lenient().when(unitOfWork.executeAtomically(any()))
                    .thenAnswer(inv -> ((java.util.function.Supplier<?>) inv.getArgument(0)).get());
            activateSupplier = new ActivateSupplier(supplierRepository, unitOfWork);
        }

        @Test
        @DisplayName("should activate an inactive supplier")
        void shouldActivateInactiveSupplier() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.of(inactiveSupplier("supplier-1"))));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var result = activateSupplier.execute(supplierId, performedBy);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().status()).isEqualTo(SupplierStatus.ACTIVE);
            verify(supplierRepository).save(any(Supplier.class));
        }

        @Test
        @DisplayName("should fail with SupplierNotFound when supplier does not exist")
        void shouldFailWhenSupplierNotFound() {
            var supplierId = SupplierId.of("nonexistent");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.empty()));

            var result = activateSupplier.execute(supplierId, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNotFound.class);
            verify(supplierRepository, never()).save(any());
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when findById fails")
        void shouldFailWhenFindByIdFails() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var result = activateSupplier.execute(supplierId, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when save fails")
        void shouldFailWhenSaveFails() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.of(inactiveSupplier("supplier-1"))));
            when(supplierRepository.save(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));

            var result = activateSupplier.execute(supplierId, performedBy);

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

    // ==================== DeactivateSupplier ====================

    @Nested
    @DisplayName("DeactivateSupplier")
    class DeactivateSupplierTest {

        private DeactivateSupplier deactivateSupplier;

        @BeforeEach
        void setUp() {
            lenient().when(unitOfWork.executeAtomically(any()))
                    .thenAnswer(inv -> ((java.util.function.Supplier<?>) inv.getArgument(0)).get());
            deactivateSupplier = new DeactivateSupplier(supplierRepository, unitOfWork);
        }

        @Test
        @DisplayName("should deactivate an active supplier")
        void shouldDeactivateActiveSupplier() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.of(existingSupplier("supplier-1"))));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var result = deactivateSupplier.execute(supplierId, performedBy);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().status()).isEqualTo(SupplierStatus.INACTIVE);
            verify(supplierRepository).save(any(Supplier.class));
        }

        @Test
        @DisplayName("should fail with SupplierNotFound when supplier does not exist")
        void shouldFailWhenSupplierNotFound() {
            var supplierId = SupplierId.of("nonexistent");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.empty()));

            var result = deactivateSupplier.execute(supplierId, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNotFound.class);
            verify(supplierRepository, never()).save(any());
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when findById fails")
        void shouldFailWhenFindByIdFails() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var result = deactivateSupplier.execute(supplierId, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when save fails")
        void shouldFailWhenSaveFails() {
            var supplierId = SupplierId.of("supplier-1");
            when(supplierRepository.findById(supplierId))
                    .thenReturn(Result.success(Optional.of(existingSupplier("supplier-1"))));
            when(supplierRepository.save(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));

            var result = deactivateSupplier.execute(supplierId, performedBy);

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

    // ==================== RateSupplier ====================

    @Nested
    @DisplayName("RateSupplier")
    class RateSupplierTest {

        private RateSupplier rateSupplier;

        @BeforeEach
        void setUp() {
            lenient().when(unitOfWork.executeAtomically(any()))
                    .thenAnswer(inv -> ((java.util.function.Supplier<?>) inv.getArgument(0)).get());
            rateSupplier = new RateSupplier(supplierRepository, unitOfWork);
        }

        @Test
        @DisplayName("should rate supplier with valid scores")
        void shouldRateSupplierWithValidScores() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var cmd = new RateSupplierCommand(supplierId, 4, 5, 3);

            var result = rateSupplier.execute(cmd, performedBy);

            assertThat(result.isSuccess()).isTrue();
            var rating = result.unsafeGetValue().rating();
            assertThat(rating).isNotNull();
            assertThat(rating.qualityScore()).isEqualTo(4);
            assertThat(rating.deliveryScore()).isEqualTo(5);
            assertThat(rating.priceScore()).isEqualTo(3);
        }

        @Test
        @DisplayName("should fail with SupplierNotFound when supplier does not exist")
        void shouldFailWhenSupplierNotFound() {
            when(supplierRepository.findById(SupplierId.of("nonexistent")))
                    .thenReturn(Result.success(Optional.empty()));

            var cmd = new RateSupplierCommand("nonexistent", 4, 5, 3);

            var result = rateSupplier.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNotFound.class);
        }

        @Test
        @DisplayName("should fail with InvalidRating when score is out of range")
        void shouldFailWhenScoreIsOutOfRange() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));

            var cmd = new RateSupplierCommand(supplierId, 0, 5, 3);

            var result = rateSupplier.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.InvalidRating.class);
        }

        @Test
        @DisplayName("should fail with InvalidRating when score exceeds maximum")
        void shouldFailWhenScoreExceedsMax() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));

            var cmd = new RateSupplierCommand(supplierId, 4, 6, 3);

            var result = rateSupplier.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.InvalidRating.class);
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when findById fails")
        void shouldFailWhenFindByIdFails() {
            when(supplierRepository.findById(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var cmd = new RateSupplierCommand("supplier-1", 4, 5, 3);

            var result = rateSupplier.execute(cmd, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when save fails")
        void shouldFailWhenSaveFails() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));
            when(supplierRepository.save(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));

            var cmd = new RateSupplierCommand(supplierId, 4, 5, 3);

            var result = rateSupplier.execute(cmd, performedBy);

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

    // ==================== AddCertificate ====================

    @Nested
    @DisplayName("AddCertificate")
    class AddCertificateTest {

        private AddCertificate addCertificate;

        @BeforeEach
        void setUp() {
            lenient().when(unitOfWork.executeAtomically(any()))
                    .thenAnswer(inv -> ((java.util.function.Supplier<?>) inv.getArgument(0)).get());
            addCertificate = new AddCertificate(supplierRepository, unitOfWork);
        }

        @Test
        @DisplayName("should add certificate to supplier")
        void shouldAddCertificateToSupplier() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var cmd = new AddCertificateCommand(
                    supplierId, "ISO 9001", "TUV", LocalDate.of(2025, 1, 1), LocalDate.of(2027, 12, 31)
            );

            var result = addCertificate.execute(cmd, performedBy);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().certificates()).hasSize(1);
            assertThat(result.unsafeGetValue().certificates().getFirst().certificateType()).isEqualTo("ISO 9001");
        }

        @Test
        @DisplayName("should fail with SupplierNotFound when supplier does not exist")
        void shouldFailWhenSupplierNotFound() {
            when(supplierRepository.findById(SupplierId.of("nonexistent")))
                    .thenReturn(Result.success(Optional.empty()));

            var cmd = new AddCertificateCommand(
                    "nonexistent", "ISO 9001", "TUV", LocalDate.of(2025, 1, 1), LocalDate.of(2027, 12, 31)
            );

            var result = addCertificate.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNotFound.class);
        }

        @Test
        @DisplayName("should fail with ValidationFailure when certificate type is blank")
        void shouldFailWhenCertificateTypeIsBlank() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));

            var cmd = new AddCertificateCommand(
                    supplierId, "", "TUV", LocalDate.of(2025, 1, 1), LocalDate.of(2027, 12, 31)
            );

            var result = addCertificate.execute(cmd, performedBy);

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

        @Test
        @DisplayName("should fail with ValidationFailure when validUntil is before validFrom")
        void shouldFailWhenValidUntilBeforeValidFrom() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));

            var cmd = new AddCertificateCommand(
                    supplierId, "ISO 9001", "TUV", LocalDate.of(2027, 1, 1), LocalDate.of(2025, 12, 31)
            );

            var result = addCertificate.execute(cmd, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when findById fails")
        void shouldFailWhenFindByIdFails() {
            when(supplierRepository.findById(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var cmd = new AddCertificateCommand(
                    "supplier-1", "ISO 9001", "TUV", LocalDate.of(2025, 1, 1), LocalDate.of(2027, 12, 31)
            );

            var result = addCertificate.execute(cmd, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when save fails")
        void shouldFailWhenSaveFails() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));
            when(supplierRepository.save(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));

            var cmd = new AddCertificateCommand(
                    supplierId, "ISO 9001", "TUV", LocalDate.of(2025, 1, 1), LocalDate.of(2027, 12, 31)
            );

            var result = addCertificate.execute(cmd, performedBy);

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

    // ==================== RemoveCertificate ====================

    @Nested
    @DisplayName("RemoveCertificate")
    class RemoveCertificateTest {

        private RemoveCertificate removeCertificate;

        @BeforeEach
        void setUp() {
            lenient().when(unitOfWork.executeAtomically(any()))
                    .thenAnswer(inv -> ((java.util.function.Supplier<?>) inv.getArgument(0)).get());
            removeCertificate = new RemoveCertificate(supplierRepository, unitOfWork);
        }

        @Test
        @DisplayName("should remove existing certificate from supplier")
        void shouldRemoveExistingCertificate() {
            var supplierId = "supplier-1";
            var validFrom = LocalDate.of(2025, 1, 1);
            var certificate = new QualityCertificate("ISO 9001", "TUV", validFrom, LocalDate.of(2027, 12, 31));
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplierWithCertificate(supplierId, certificate))));
            when(supplierRepository.save(any())).thenReturn(Result.success(null));

            var cmd = new RemoveCertificateCommand(supplierId, "ISO 9001", "TUV", validFrom);

            var result = removeCertificate.execute(cmd, performedBy);

            assertThat(result.isSuccess()).isTrue();
            assertThat(result.unsafeGetValue().certificates()).isEmpty();
        }

        @Test
        @DisplayName("should fail with CertificateNotFound when certificate does not exist")
        void shouldFailWhenCertificateNotFound() {
            var supplierId = "supplier-1";
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplier(supplierId))));

            var cmd = new RemoveCertificateCommand(
                    supplierId, "ISO 9001", "TUV", LocalDate.of(2025, 1, 1)
            );

            var result = removeCertificate.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.CertificateNotFound.class);
        }

        @Test
        @DisplayName("should fail with SupplierNotFound when supplier does not exist")
        void shouldFailWhenSupplierNotFound() {
            when(supplierRepository.findById(SupplierId.of("nonexistent")))
                    .thenReturn(Result.success(Optional.empty()));

            var cmd = new RemoveCertificateCommand(
                    "nonexistent", "ISO 9001", "TUV", LocalDate.of(2025, 1, 1)
            );

            var result = removeCertificate.execute(cmd, performedBy);

            assertThat(result.isFailure()).isTrue();
            assertThat(result.unsafeGetError()).isInstanceOf(SupplierError.SupplierNotFound.class);
        }

        @Test
        @DisplayName("should fail with RepositoryFailure when findById fails")
        void shouldFailWhenFindByIdFails() {
            when(supplierRepository.findById(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));

            var cmd = new RemoveCertificateCommand(
                    "supplier-1", "ISO 9001", "TUV", LocalDate.of(2025, 1, 1)
            );

            var result = removeCertificate.execute(cmd, performedBy);

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

        @Test
        @DisplayName("should fail with RepositoryFailure when save fails")
        void shouldFailWhenSaveFails() {
            var supplierId = "supplier-1";
            var validFrom = LocalDate.of(2025, 1, 1);
            var certificate = new QualityCertificate("ISO 9001", "TUV", validFrom, LocalDate.of(2027, 12, 31));
            when(supplierRepository.findById(SupplierId.of(supplierId)))
                    .thenReturn(Result.success(Optional.of(existingSupplierWithCertificate(supplierId, certificate))));
            when(supplierRepository.save(any()))
                    .thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));

            var cmd = new RemoveCertificateCommand(supplierId, "ISO 9001", "TUV", validFrom);

            var result = removeCertificate.execute(cmd, performedBy);

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