package org.implab.gradle.variants.sources.internal;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import org.gradle.api.InvalidUserDataException;
import org.implab.gradle.variants.core.Layer;
import org.implab.gradle.variants.core.Variant;
import org.implab.gradle.variants.sources.CompileUnit;
import org.junit.jupiter.api.Test;

class CompileUnitNamerTest {
    @Test
    void resolvesProjectedNameForUniqueCompileUnit() {
        var unit = unit("browser", "main");

        var namer = CompileUnitNamer.builder()
                .addUnits(List.of(unit))
                .build();

        assertEquals("browserMain", namer.resolveName(unit));
    }

    @Test
    void failsOnProjectedNameCollisionInFailMode() {
        var left = unit("foo", "variantBar");
        var right = unit("fooVariant", "bar");

        var ex = assertThrows(
                InvalidUserDataException.class,
                () -> CompileUnitNamer.builder()
                        .addUnits(List.of(left, right))
                        .nameCollisionPolicy(NameCollisionPolicy.FAIL)
                        .build());

        assertTrue(ex.getMessage().contains("The same source set names are produced by different compile units"));
        assertTrue(ex.getMessage().contains("fooVariantBar"));
    }

    @Test
    void resolvesProjectedNameCollisionDeterministicallyInCanonicalOrder() {
        var earlier = unit("foo", "variantBar");
        var later = unit("fooVariant", "bar");

        var namer = CompileUnitNamer.builder()
                .addUnits(List.of(later, earlier))
                .nameCollisionPolicy(NameCollisionPolicy.RESOLVE)
                .build();

        assertEquals("fooVariantBar", namer.resolveName(earlier));
        assertEquals("fooVariantBar2", namer.resolveName(later));
    }

    @Test
    void rejectsUnknownCompileUnitLookup() {
        var known = unit("browser", "main");
        var unknown = unit("browser", "test");

        var namer = CompileUnitNamer.builder()
                .addUnits(List.of(known))
                .build();

        var ex = assertThrows(InvalidUserDataException.class, () -> namer.resolveName(unknown));
        assertTrue(ex.getMessage().contains("Compile unit"));
        assertTrue(ex.getMessage().contains("associated name"));
    }

    private static CompileUnit unit(String variantName, String layerName) {
        return new CompileUnit(
                new TestVariant(variantName),
                new TestLayer(layerName));
    }

    private record TestVariant(String value) implements Variant {
        @Override
        public String getName() {
            return value;
        }
    }

    private record TestLayer(String value) implements Layer {
        @Override
        public String getName() {
            return value;
        }
    }
}
