package uk.ac.aber.cs31920.assignment.tests.solvers;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import uk.ac.aber.cs31920.assignment.implementation.solvers.BreadthFirstSearchRunner;
import uk.ac.aber.cs31920.assignment.implementation.datastructures.GraphNode;

import java.util.ArrayList;
import java.util.List;

public class BreadthFirstSearchRunnerTests {
@Test
    public void testSearch(){
    /*
        graph
        ┌─┐     ┌─┐     ┌─┐
        │1├────►│2├────►│4│
        └─┘     └┬┘     └─┘
                 │
                 ▼
                ┌─┐
                │3│
                └─┘
    */

    // arrange
    GraphNode node4 = new GraphNode(null, null, 4);
    GraphNode node3 = new GraphNode(null, null, 3);
    GraphNode node2 = new GraphNode(new GraphNode[]{node3, node4}, null, 2);
    GraphNode node1 = new GraphNode(new GraphNode[]{node2}, null, 1);
    node1.setAsSource();

    // act
    List<GraphNode> result = BreadthFirstSearchRunner.run(node1, node3, GraphNode::getNetworkChildren);

    // assert
    List<GraphNode> expected = new ArrayList<>(List.of(node1, node2, node3));
    Assertions.assertEquals(expected, result);
    }

    @Test
    public void testSearchWithCircularConnections() {
    /*
        graph
        ┌─┐     ┌─┐     ┌─┐
        │1├────►│2├────►│4│
        └─┘   ▲ └┬┘     └┬┘
              │  │       │
              │  ▼       │
              │ ┌─┐      │
              └─┤3│◄─────┘
                └─┘
    */

        // arrange
        GraphNode node4 = new GraphNode(null, null, 4);
        GraphNode node3 = new GraphNode(null, null, 3);
        GraphNode node2 = new GraphNode(new GraphNode[]{node3, node4}, null, 2);
        GraphNode node1 = new GraphNode(new GraphNode[]{node2}, null, 1);
        node4.addNetworkChild(node3);
        node3.addNetworkChild(node2);
        node1.setAsSource();

        // act
        List<GraphNode> result1 = BreadthFirstSearchRunner.run(node1, node4, GraphNode::getNetworkChildren);
        List<GraphNode> result2 = BreadthFirstSearchRunner.run(node1, node3, GraphNode::getNetworkChildren);

        // assert
        List<GraphNode> expected1 = new ArrayList<>(List.of(node1, node2, node4));
        List<GraphNode> expected2 = new ArrayList<>(List.of(node1, node2, node3));
        Assertions.assertEquals(expected1, result1);
        Assertions.assertEquals(expected2, result2);
    }

        @Test
        public void testSearchWithMatchingProblemGraph(){
        /*
                graph
                 ┌─┐       ┌─┐
             ┌──►│2├───┐   │5├───┐
             │   └─┘   │┌─►└─┘   │
             │       ┌─┼┘        ▼
            ┌┴┐  ┌─┐ │ └──►┌─┐  ┌─┐
            │1├─►│3├─┼────►│6├─►│8│
            └┬┘  └─┘ │ ┌──►└─┘  └▲┘
             │       └─┼┐        │
             │   ┌─┐   │└─►┌─┐   │
             └──►│4├───┘   │7├───┘
                 └─┘       └─┘
        */

            // arrange
            GraphNode node1 = new GraphNode(null, null, 1);
            GraphNode node2 = new GraphNode(null, null, 2);
            GraphNode node3 = new GraphNode(null, null, 3);
            GraphNode node4 = new GraphNode(null, null, 4);
            GraphNode node5 = new GraphNode(null, null, 5);
            GraphNode node6 = new GraphNode(null, null, 6);
            GraphNode node7 = new GraphNode(null, null, 7);
            GraphNode node8 = new GraphNode(null, null, 8);
            node1.addNetworkChildren(new GraphNode[]{node2, node3, node4});node1.setAsSource();
            node2.addNetworkChild(node6);
            node3.addNetworkChildren(new GraphNode[]{node5, node6, node7});
            node4.addNetworkChild(node6);
            node5.addNetworkChild(node8);
            node6.addNetworkChild(node8);
            node7.addNetworkChild(node8);node8.setAsSink();

            // act
            List<GraphNode> result1 = BreadthFirstSearchRunner.run(node1, node8, GraphNode::getNetworkChildren);
            List<GraphNode> result2 = BreadthFirstSearchRunner.run(node1, node7, GraphNode::getNetworkChildren);

            // assert
            List<GraphNode> expected1 = new ArrayList<>(List.of(node1, node2, node6, node8));
            List<GraphNode> expected2 = new ArrayList<>(List.of(node1, node3, node7));
            Assertions.assertEquals(expected1, result1);
            Assertions.assertEquals(expected2, result2);
        }
}
