package org.implab.gradle.variants;

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

import org.gradle.testkit.runner.BuildResult;
import org.junit.jupiter.api.Test;

class VariantsPluginFunctionalTest extends AbstractFunctionalTest {

    @Test
    void replaysFinalizedViewAndExposesResolvedEntries() throws Exception {
        writeSettings("variants-current-contract");
        writeBuildFile("""
                apply plugin: org.implab.gradle.variants.VariantsPlugin

                def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
                variantsExt.layers.create('main')
                variantsExt.layers.create('test')
                variantsExt.roles.create('production')
                variantsExt.roles.create('test')

                variantsExt.variant('browser') {
                    role('production') { layers('main') }
                    role('test') { layers('main', 'test') }
                }

                def events = []

                variantsExt.whenFinalized { view ->
                    events << "early:entries=" + view.entries
                        .collect { "${it.variant().name}:${it.role().name}:${it.layer().name}" }
                        .sort()
                        .join('|')
                    events << "early:layers=" + view.layers.collect { it.name }.sort().join(',')
                    def production = view.roles.find { it.name == 'production' }
                    def mainLayer = view.layers.find { it.name == 'main' }
                    events << "early:productionEntries=" + view.getEntriesForRole(production).size()
                    events << "early:mainEntries=" + view.getEntriesForLayer(mainLayer).size()
                }

                afterEvaluate {
                    variantsExt.whenFinalized { view ->
                        events << "late:variants=" + view.variants.collect { it.name }.sort().join(',')
                    }
                }

                tasks.register('probe') {
                    doLast {
                        events.each { println(it) }
                    }
                }
                """);

        BuildResult result = runner("probe").build();

        assertTrue(result.getOutput().contains(
                "early:entries=browser:production:main|browser:test:main|browser:test:test"));
        assertTrue(result.getOutput().contains("early:layers=main,test"));
        assertTrue(result.getOutput().contains("early:productionEntries=1"));
        assertTrue(result.getOutput().contains("early:mainEntries=2"));
        assertTrue(result.getOutput().contains("late:variants=browser"));
    }

    @Test
    void exposesDeclaredButUnboundIdentitiesInFinalizedView() throws Exception {
        writeSettings("variants-unbound-identities");
        writeBuildFile("""
                apply plugin: org.implab.gradle.variants.VariantsPlugin

                def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
                variantsExt.layers.create('main')
                variantsExt.layers.create('test')
                variantsExt.roles.create('production')
                variantsExt.roles.create('test')

                variantsExt.variant('browser') {
                    role('production') { layers('main') }
                }
                variantsExt.variant('node')

                tasks.register('probe') {
                    doLast {
                        variantsExt.whenFinalized { view ->
                            def testRole = view.roles.find { it.name == 'test' }
                            def testLayer = view.layers.find { it.name == 'test' }

                            println("variants=" + view.variants.collect { it.name }.sort().join(','))
                            println("entriesForTestRole=" + view.getEntriesForRole(testRole).size())
                            println("entriesForTestLayer=" + view.getEntriesForLayer(testLayer).size())
                        }
                    }
                }
                """);

        BuildResult result = runner("probe").build();

        assertTrue(result.getOutput().contains("variants=browser,node"));
        assertTrue(result.getOutput().contains("entriesForTestRole=0"));
        assertTrue(result.getOutput().contains("entriesForTestLayer=0"));
    }

    @Test
    void failsOnUnknownLayerReferenceDuringFinalization() throws Exception {
        writeSettings("variants-missing-layer");
        writeBuildFile("""
                apply plugin: org.implab.gradle.variants.VariantsPlugin

                def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
                variantsExt.layers.create('main')
                variantsExt.roles.create('production')

                variantsExt.variant('browser') {
                    role('production') { layers('main', 'missingLayer') }
                }
                """);

        assertBuildFails("Layer 'missingLayer' isn't declared, referenced in 'browser' variant with 'production' role", "help");
    }

    @Test
    void failsOnUnknownRoleReferenceDuringFinalization() throws Exception {
        writeSettings("variants-missing-role");
        writeBuildFile("""
                apply plugin: org.implab.gradle.variants.VariantsPlugin

                def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
                variantsExt.layers.create('main')

                variantsExt.variant('browser') {
                    role('production') { layers('main') }
                }
                """);

        assertBuildFails("Role 'production' isn't declared, referenced in 'browser' variant", "help");
    }
}
