package com.graphtest;

import com.graphtest.model.Node;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

import static org.assertj.core.api.Assertions.*;

@DisplayName("Category 3: Các tính chất của Graph (Graph Properties)")
class Category3_GraphPropertiesTest {

    private CodeGraph graph;

    @BeforeEach
    void setUp() throws Exception {
        URL resource = getClass().getClassLoader().getResource("fixtures/cat3_properties");
        Path dir = Paths.get(resource.toURI());
        graph = new GraphBuilder().buildFromDirectory(dir);
    }

    // --- ROOT NODES ---

    @Test
    @DisplayName("EntryPoint là root node vì không ai import nó")
    void entryPointShouldBeRootNode() {
        List<Node> roots = graph.getRootNodes();
        assertThat(roots).extracting(Node::getClassName)
            .contains("EntryPoint");
    }

    @Test
    @DisplayName("MiddleNode và LeafNode không phải root node vì được import bởi node khác")
    void middleAndLeafShouldNotBeRootNodes() {
        List<Node> roots = graph.getRootNodes();
        assertThat(roots).extracting(Node::getClassName)
            .doesNotContain("MiddleNode", "LeafNode");
    }

    // --- LEAF NODES ---

    @Test
    @DisplayName("LeafNode là leaf vì không có outgoing edges")
    void leafNodeShouldBeDetectedAsLeaf() {
        List<Node> leaves = graph.getLeafNodes();
        assertThat(leaves).extracting(Node::getClassName)
            .contains("LeafNode");
    }

    @Test
    @DisplayName("EntryPoint và MiddleNode không phải leaf vì có outgoing edges")
    void entryAndMiddleShouldNotBeLeafNodes() {
        List<Node> leaves = graph.getLeafNodes();
        assertThat(leaves).extracting(Node::getClassName)
            .doesNotContain("EntryPoint", "MiddleNode");
    }

    // --- ISOLATED NODES ---

    @Test
    @DisplayName("IsolatedNode phải được phát hiện là node cô lập")
    void isolatedNodeShouldBeDetected() {
        List<Node> isolated = graph.getIsolatedNodes();
        assertThat(isolated).extracting(Node::getClassName)
            .contains("IsolatedNode");
    }

    @Test
    @DisplayName("Các node có kết nối không phải là isolated")
    void connectedNodesShouldNotBeIsolated() {
        List<Node> isolated = graph.getIsolatedNodes();
        assertThat(isolated).extracting(Node::getClassName)
            .doesNotContain("EntryPoint", "MiddleNode", "LeafNode");
    }

    // --- CONNECTIVITY ---

    @Test
    @DisplayName("Chain EntryPoint → MiddleNode → LeafNode phải đúng")
    void chainShouldBeCorrect() {
        assertThat(graph.hasEdge("EntryPoint", "MiddleNode")).isTrue();
        assertThat(graph.hasEdge("MiddleNode", "LeafNode")).isTrue();
        assertThat(graph.hasEdge("EntryPoint", "LeafNode")).isFalse();
    }

    @Test
    @DisplayName("Graph không có cycle trong cấu trúc tuyến tính")
    void linearGraphShouldHaveNoCycles() {
        assertThat(graph.getDetectedCycles()).isEmpty();
    }

    // --- INCOMING EDGES ---

    @Test
    @DisplayName("LeafNode có đúng 1 incoming edge")
    void leafNodeShouldHaveOneIncomingEdge() {
        assertThat(graph.getIncomingEdges("LeafNode")).hasSize(1);
    }

    @Test
    @DisplayName("EntryPoint không có incoming edge")
    void entryPointShouldHaveNoIncomingEdges() {
        assertThat(graph.getIncomingEdges("EntryPoint")).isEmpty();
    }
}
