package de.avatic.lcc.repositories;

import de.avatic.lcc.model.db.packaging.PackagingDimension;
import de.avatic.lcc.model.db.packaging.PackagingType;
import de.avatic.lcc.model.db.utils.DimensionUnit;
import de.avatic.lcc.model.db.utils.WeightUnit;
import de.avatic.lcc.repositories.packaging.PackagingDimensionRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Integration tests for PackagingDimensionRepository.
 * <p>
 * Tests critical functionality across both MySQL and MSSQL:
 * - CRUD operations
 * - Boolean literal compatibility (deprecated filtering)
 * - Enum handling (PackagingType, DimensionUnit, WeightUnit)
 * <p>
 * Run with:
 * <pre>
 * mvn test -Dspring.profiles.active=test,mysql -Dtest=PackagingDimensionRepositoryIntegrationTest
 * mvn test -Dspring.profiles.active=test,mssql -Dtest=PackagingDimensionRepositoryIntegrationTest
 * </pre>
 */
class PackagingDimensionRepositoryIntegrationTest extends AbstractRepositoryIntegrationTest {

    @Autowired
    private PackagingDimensionRepository packagingDimensionRepository;

    @Test
    void testInsertAndRetrieve() {
        // Given: Create packaging dimension
        PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1);

        // When: Insert
        Optional<Integer> dimensionId = packagingDimensionRepository.insert(dimension);

        // Then: Should be inserted successfully
        assertTrue(dimensionId.isPresent(), "Dimension ID should be present");
        assertTrue(dimensionId.get() > 0, "Dimension ID should be positive");

        // When: Retrieve by ID
        Optional<PackagingDimension> retrieved = packagingDimensionRepository.getById(dimensionId.get());

        // Then: Should retrieve successfully
        assertTrue(retrieved.isPresent(), "Dimension should be retrievable after insert");
        assertEquals(1000, retrieved.get().getLength());
        assertEquals(500, retrieved.get().getWidth());
        assertEquals(300, retrieved.get().getHeight());
        assertEquals(10000, retrieved.get().getWeight());
        assertEquals(1, retrieved.get().getContentUnitCount());
        assertEquals(PackagingType.HU, retrieved.get().getType());
        assertEquals(DimensionUnit.CM, retrieved.get().getDimensionUnit());
        assertEquals(WeightUnit.KG, retrieved.get().getWeightUnit());
        assertFalse(retrieved.get().getDeprecated());
    }

    @Test
    void testUpdate() {
        // Given: Insert dimension
        PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1);
        Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow();

        // When: Update dimension
        PackagingDimension toUpdate = packagingDimensionRepository.getById(dimensionId).orElseThrow();
        toUpdate.setLength(1200);
        toUpdate.setWidth(600);
        toUpdate.setHeight(400);
        toUpdate.setWeight(15000);
        toUpdate.setContentUnitCount(2);
        packagingDimensionRepository.update(toUpdate);

        // Then: Verify update
        PackagingDimension updated = packagingDimensionRepository.getById(dimensionId).orElseThrow();
        assertEquals(1200, updated.getLength());
        assertEquals(600, updated.getWidth());
        assertEquals(400, updated.getHeight());
        assertEquals(15000, updated.getWeight());
        assertEquals(2, updated.getContentUnitCount());
    }

    @Test
    void testSetDeprecatedById() {
        // Given: Insert dimension
        PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1);
        Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow();

        // When: Deprecate
        Optional<Integer> result = packagingDimensionRepository.setDeprecatedById(dimensionId);

        // Then: Should be deprecated
        assertTrue(result.isPresent());

        // getById() excludes deprecated dimensions
        Optional<PackagingDimension> deprecated = packagingDimensionRepository.getById(dimensionId);
        assertFalse(deprecated.isPresent(), "Deprecated dimension should not be retrievable via getById()");
    }

    @Test
    void testGetByIdNotFound() {
        // When: Get by non-existent ID
        Optional<PackagingDimension> result = packagingDimensionRepository.getById(99999);

        // Then: Should return empty
        assertFalse(result.isPresent(), "Should not find dimension with non-existent ID");
    }

    @Test
    void testDifferentPackagingTypes() {
        // Given: Insert dimensions with different types
        PackagingDimension hu = createTestDimension(1000, 500, 300, 10000, 1);
        hu.setType(PackagingType.HU);
        Integer huId = packagingDimensionRepository.insert(hu).orElseThrow();

        PackagingDimension shu = createTestDimension(500, 300, 200, 5000, 1);
        shu.setType(PackagingType.SHU);
        Integer shuId = packagingDimensionRepository.insert(shu).orElseThrow();

        // When: Retrieve both
        PackagingDimension retrievedHu = packagingDimensionRepository.getById(huId).orElseThrow();
        PackagingDimension retrievedShu = packagingDimensionRepository.getById(shuId).orElseThrow();

        // Then: Should have correct types
        assertEquals(PackagingType.HU, retrievedHu.getType());
        assertEquals(PackagingType.SHU, retrievedShu.getType());
    }

    @Test
    void testDifferentUnits() {
        // Given: Insert dimension with different units (meters and grams instead of cm and kg)
        PackagingDimension dimension = createTestDimension(1, 1, 1, 1000, 1); // meters and grams
        dimension.setDimensionUnit(DimensionUnit.M);
        dimension.setWeightUnit(WeightUnit.G);
        Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow();

        // When: Retrieve
        PackagingDimension retrieved = packagingDimensionRepository.getById(dimensionId).orElseThrow();

        // Then: Should have correct units
        assertEquals(DimensionUnit.M, retrieved.getDimensionUnit());
        assertEquals(WeightUnit.G, retrieved.getWeightUnit());
        assertEquals(1, retrieved.getLength());
        assertEquals(1, retrieved.getWidth());
        assertEquals(1, retrieved.getHeight());
        assertEquals(1000, retrieved.getWeight());
    }

    @Test
    void testUpdateWithDeprecation() {
        // Given: Insert dimension
        PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1);
        Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow();

        // When: Update and deprecate
        PackagingDimension toUpdate = packagingDimensionRepository.getById(dimensionId).orElseThrow();
        toUpdate.setDeprecated(true);
        packagingDimensionRepository.update(toUpdate);

        // Then: Should not be retrievable via getById() (which filters deprecated)
        Optional<PackagingDimension> deprecated = packagingDimensionRepository.getById(dimensionId);
        assertFalse(deprecated.isPresent());
    }

    // ========== Helper Methods ==========

    private PackagingDimension createTestDimension(Integer length, Integer width, Integer height, Integer weight, Integer contentUnitCount) {
        PackagingDimension dimension = new PackagingDimension();
        dimension.setType(PackagingType.HU);
        dimension.setLength(length);
        dimension.setWidth(width);
        dimension.setHeight(height);
        dimension.setDimensionUnit(DimensionUnit.CM);
        dimension.setWeight(weight);
        dimension.setWeightUnit(WeightUnit.KG);
        dimension.setContentUnitCount(contentUnitCount);
        dimension.setDeprecated(false);
        return dimension;
    }
}
