package com.blackduck.integration.detectable.detectables.cargo.functional;

import java.io.IOException;
import java.nio.file.Paths;

import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;

import com.blackduck.integration.bdio.model.Forge;
import com.blackduck.integration.detectable.Detectable;
import com.blackduck.integration.detectable.DetectableEnvironment;
import com.blackduck.integration.detectable.extraction.Extraction;
import com.blackduck.integration.detectable.functional.DetectableFunctionalTest;
import com.blackduck.integration.detectable.util.graph.NameVersionGraphAssert;

public class CargoLockBomComponentCountTest extends DetectableFunctionalTest {

    public CargoLockBomComponentCountTest() throws IOException {
        super("cargo-workspace-orphans");
    }

    @Override
    protected void setup() throws IOException {
        // Cargo.toml: workspace + regular, dev, build, and target-specific deps
        addFile(
            Paths.get("Cargo.toml"),
            "[package]",
            "name = \"ripgrep\"",
            "version = \"14.1.1\"",
            "edition = \"2021\"",
            "",
            "[workspace]",
            "members = [",
            "  \"crates/grep\",",
            "  \"crates/cli\"",
            "]",
            "",
            "[dependencies]",
            "anyhow = \"1.0.75\"",
            "bstr = \"1.7.0\"",
            "grep = { version = \"0.3.2\", path = \"crates/grep\" }",
            "serde_json = \"1.0.23\"",
            "termcolor = \"1.1.0\"",
            "",
            "[target.'cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))'.dependencies.jemallocator]",
            "version = \"0.5.0\"",
            "",
            "[dev-dependencies]",
            "serde = \"1.0.77\"",
            "serde_derive = \"1.0.77\"",
            "walkdir = \"2.0.0\"",
            "",
            "[build-dependencies]",
            "cc = \"1.0\""
        );

        // Cargo.lock: includes all deps from Cargo.toml plus some orphans
        addFile(
            Paths.get("Cargo.lock"),
            "# This file is automatically @generated by Cargo.",
            "version = 4",
            "",
            // Regular deps
            "[[package]]",
            "name = \"anyhow\"",
            "version = \"1.0.75\"",
            "",
            "[[package]]",
            "name = \"bstr\"",
            "version = \"1.7.0\"",
            "dependencies = [ \"regex\" ]",
            "",
            "[[package]]",
            "name = \"regex\"",
            "version = \"1.10.0\"",
            "",
            "[[package]]",
            "name = \"grep\"",
            "version = \"0.3.2\"",
            "",
            "[[package]]",
            "name = \"serde_json\"",
            "version = \"1.0.23\"",
            "",
            "[[package]]",
            "name = \"termcolor\"",
            "version = \"1.1.0\"",
            "",
            // Target-specific dep
            "[[package]]",
            "name = \"jemallocator\"",
            "version = \"0.5.0\"",
            "",
            // Dev dep (explicitly checking one dev dep is enough here)
            "[[package]]",
            "name = \"serde\"",
            "version = \"1.0.77\"",
            "",
            // Build dep + its child
            "[[package]]",
            "name = \"cc\"",
            "version = \"1.2.29\"",
            "dependencies = [ \"shlex\" ]",
            "",
            "[[package]]",
            "name = \"shlex\"",
            "version = \"1.3.0\"",
            "",
            // Orphan chain (not present in Cargo.toml)
            "[[package]]",
            "name = \"errno\"",
            "version = \"0.3.13\"",
            "dependencies = [ \"libc\" ]",
            "",
            "[[package]]",
            "name = \"libc\"",
            "version = \"0.2.174\""
        );
    }

    @NotNull
    @Override
    public Detectable create(@NotNull DetectableEnvironment detectableEnvironment) {
        return detectableFactory.createCargoLockfileDetectable(detectableEnvironment);
    }

    @Override
    public void assertExtraction(@NotNull Extraction extraction) {
        // Basic sanity
        Assertions.assertEquals(1, extraction.getCodeLocations().size());
        Assertions.assertEquals("ripgrep", extraction.getProjectName());
        Assertions.assertEquals("14.1.1", extraction.getProjectVersion());

        NameVersionGraphAssert graphAssert =
            new NameVersionGraphAssert(Forge.CRATES, extraction.getCodeLocations().get(0).getDependencyGraph());

        // Roots from Cargo.toml [dependencies]
        graphAssert.hasRootDependency("anyhow", "1.0.75");
        graphAssert.hasRootDependency("bstr", "1.7.0");
        graphAssert.hasRootDependency("grep", "0.3.2");
        graphAssert.hasRootDependency("serde_json", "1.0.23");
        graphAssert.hasRootDependency("termcolor", "1.1.0");

        // Target-specific root dep
        graphAssert.hasRootDependency("jemallocator", "0.5.0");

        // Dev + build roots
        graphAssert.hasRootDependency("serde", "1.0.77");
        graphAssert.hasRootDependency("cc", "1.2.29");

        // Orphans bucket + chain
        graphAssert.hasRootDependency("Additional_Components", "none");
        graphAssert.hasParentChildRelationship("Additional_Components", "none", "errno", "0.3.13");

        // Verify a transitive edge from lock
        graphAssert.hasParentChildRelationship("bstr", "1.7.0", "regex", "1.10.0");
        graphAssert.hasParentChildRelationship("cc", "1.2.29", "shlex", "1.3.0");

        // Root size: 5 ([dependencies]) + 1 (target) + 2 (dev/build) + 1 (orphan/synthetic) = 9
        graphAssert.hasRootSize(9);
    }
}
