import javafx.scene.control.*;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;

import java.io.IOException;
import java.util.List;

import org.junit.jupiter.api.Assertions;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.testfx.framework.junit5.ApplicationTest;
import org.testfx.api.FxRobotInterface;

import static org.junit.Assert.fail;

public class BackendDeveloperTests extends ApplicationTest {

    private BackendInterface backend = new Backend(new DijkstraGraph<>());
    private FrontendInterface frontend = new Frontend();

    /**
     * Test the loadGraphData method.
     */
    @Test
    public void testLoadGraphData() {
        try {
            backend.loadGraphData("campusForTest.dot");
        } catch (IOException e) {
            fail("IOException should not have been thrown");
        }
    }

    /**
     * Test the loadGraphData method with an IOException.
     */
    @Test
    public void testLoadGraphDataIOException() {
        try {
            backend.loadGraphData("nonexistentFile.dot");
            fail("IOException should have been thrown");
        } catch (IOException e) {
            // expected
        }
    }

    /**
     * Test the getListOfAllLocations method.
     */
    @Test
    public void testGetListOfAllLocations() {
        try {
            backend.loadGraphData("campusForTest.dot");
            List<String> output = backend.getListOfAllLocations();
            Assertions.assertTrue(output.contains("Memorial Union"));
            Assertions.assertTrue(output.contains("Science Hall"));
            Assertions.assertTrue(output.contains("Brat Stand"));
            Assertions.assertTrue(output.contains("Helen C White Hall"));
            Assertions.assertTrue(output.contains("Radio Hall"));
            Assertions.assertTrue(output.contains("Wisconsin State Historical Society"));
            Assertions.assertTrue(output.contains("Education Building"));
            Assertions.assertTrue(output.contains("North Hall"));
            Assertions.assertTrue(output.contains("South Hall"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Test the findShortestPath method.
     */
    @Test
    public void testFindShortestPath() {
        try {
            backend.loadGraphData("campusForTest.dot");
            List<String> path = backend.findShortestPath("Memorial Union", "Wisconsin State Historical Society");
            Assertions.assertEquals(3, path.size());
            Assertions.assertEquals("Memorial Union", path.get(0));
            Assertions.assertEquals("Science Hall", path.get(1));
            Assertions.assertEquals("Wisconsin State Historical Society", path.get(2));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Test the findShortestPath method with a nonexistent path.
     */
    @Test
    public void testGetTravelTimesOnPath() {
        try {
            backend.loadGraphData("campusForTest.dot");
            backend.findShortestPath("Memorial Union", "Wisconsin State Historical Society");
            List<Double> times = backend.getTravelTimesOnPath("Memorial Union", "Wisconsin State Historical Society");
            Assertions.assertEquals(105.8, times.get(0));
            Assertions.assertEquals(145.8, times.get(1));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Test the getMostDistantLocation method.
     */
    @Test
    public void testGetMostDistantLocation() {
        try {
            backend.loadGraphData("campusForTest.dot");
            String location = backend.getMostDistantLocation("Memorial Union");
            Assertions.assertEquals("South Hall", location);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Test the findShortestPath method.
     * @throws Exception
     */
    @Test
    public void IntegrationTest1() throws Exception {
        ApplicationTest.launch(Frontend.class);
        try {
            backend.loadGraphData("campus.dot");
            List<String> backendOutput = backend.findShortestPath("Memorial Union", "Wisconsin State Historical Society");
            Assertions.assertEquals(2, backendOutput.size());
            Assertions.assertEquals("Memorial Union", backendOutput.get(0));
            Assertions.assertEquals("Wisconsin State Historical Society", backendOutput.get(1));
            ComboBox srcSelector = lookup("#srcSelector").query();
            ComboBox dstSelector = lookup("#dstSelector").query();
            Button search = lookup("#searchButton").query();
            Label path = lookup("#path").query();
            interact(() -> srcSelector.getSelectionModel().select("Memorial Union"));
            interact(() -> dstSelector.getSelectionModel().select("Wisconsin State Historical Society"));
            Assertions.assertEquals("Memorial Union", srcSelector.getValue());
            Assertions.assertEquals("Wisconsin State Historical Society", dstSelector.getValue());
            clickOn(search);
            String location1 = "Memorial Union";
            String location2 = "Wisconsin State Historical Society";
            Assertions.assertTrue(path.getText().contains(location1));
            Assertions.assertTrue(path.getText().contains(location2));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Test the getMostDistantLocation method.
     * @throws Exception
     */
    @Test
    public void IntegrationTest2() throws Exception {
        ApplicationTest.launch(Frontend.class);
        try {
            backend.loadGraphData("campus.dot");
            String backendOutput = backend.getMostDistantLocation("Memorial Union");
            Assertions.assertEquals("Smith Residence Hall", backendOutput);
            ComboBox locationSelector = lookup("#locationSelector").query();
            Button search = lookup("#furthestFromButton").query();
            Label path = lookup("#furthestFromLabel").query();
            interact(() -> locationSelector.getSelectionModel().select("Memorial Union"));
            Assertions.assertEquals("Memorial Union", locationSelector.getValue());
            clickOn(search);
            System.out.println(path.getText());
            String location = "Smith Residence Hall";
            Assertions.assertTrue(path.getText().contains(location));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Test the findShortestPath method.
     * @throws Exception
     */
    @Test
    public void PartnerTest1() throws Exception {
        ApplicationTest.launch(Frontend.class);
        ComboBox srcSelector = lookup("#srcSelector").query();
        ComboBox dstSelector = lookup("#dstSelector").query();
        Button search = lookup("#searchButton").query();
        Label path = lookup("#path").query();
        interact(() -> srcSelector.getSelectionModel().select("Van Vleck Hall"));
        interact(() -> dstSelector.getSelectionModel().select("Lot 36 - Observatory Drive Ramp"));
        Assertions.assertEquals("Van Vleck Hall", srcSelector.getValue());
        Assertions.assertEquals("Lot 36 - Observatory Drive Ramp", dstSelector.getValue());
        clickOn(search);
        System.out.println(path.getText());
        String location1 = "Van Vleck Hall";
        String location2 = "Medical Sciences";
        String location3 = "Bardeen Medical Laboratories";
        String location4 = "Nutritional Sciences";
        String location5 = "Stovall Building";
        String location6 = "DeLuca Biochemical Sciences Building";
        String location7 = "DeLuca Biochemical Sciences Building";
        String location8 = "Horticulture";
        String location9 = "Russell Laboratories";
        String location10 = "Steenbock Memorial Library";
        String location11 = "Lot 36 - Observatory Drive Ramp";
        Assertions.assertTrue(path.getText().contains(location1));
        Assertions.assertTrue(path.getText().contains(location2));
        Assertions.assertTrue(path.getText().contains(location3));
        Assertions.assertTrue(path.getText().contains(location4));
        Assertions.assertTrue(path.getText().contains(location5));
        Assertions.assertTrue(path.getText().contains(location6));
        Assertions.assertTrue(path.getText().contains(location7));
        Assertions.assertTrue(path.getText().contains(location8));
        Assertions.assertTrue(path.getText().contains(location9));
        Assertions.assertTrue(path.getText().contains(location10));
        Assertions.assertTrue(path.getText().contains(location11));
    }

    /**
     * Test the findShortestPath method with showTimesBox selected.
     * @throws Exception
     */
    @Test
    public void PartnerTest2() throws Exception {
        ApplicationTest.launch(Frontend.class);
        ComboBox srcSelector = lookup("#srcSelector").query();
        ComboBox dstSelector = lookup("#dstSelector").query();
        Button search = lookup("#searchButton").query();
        CheckBox showTimesBox = lookup("#showTimesBox").query();
        Label path = lookup("#path").query();
        interact(() -> srcSelector.getSelectionModel().select("Van Vleck Hall"));
        interact(() -> dstSelector.getSelectionModel().select("Lot 36 - Observatory Drive Ramp"));
        interact(() -> showTimesBox.setSelected(true));
        Assertions.assertEquals("Van Vleck Hall", srcSelector.getValue());
        Assertions.assertEquals("Lot 36 - Observatory Drive Ramp", dstSelector.getValue());
        clickOn(search);
        System.out.println(path.getText());
        String time1 = "137.5";
        String time2 = "90.7";
        String time3 = "121.7";
        String time4 = "118.30000000000001";
        String time5 = "94.8";
        String time6 = "63.800000000000004sec";
        String time7 = "82.69999999999999";
        String time8 = "129.10000000000002";
        String time9 = "141.7";
        String time10 = "113.30000000000001";
        Assertions.assertTrue(path.getText().contains(time1));
        Assertions.assertTrue(path.getText().contains(time2));
        Assertions.assertTrue(path.getText().contains(time3));
        Assertions.assertTrue(path.getText().contains(time4));
        Assertions.assertTrue(path.getText().contains(time5));
        Assertions.assertTrue(path.getText().contains(time6));
        Assertions.assertTrue(path.getText().contains(time7));
        Assertions.assertTrue(path.getText().contains(time8));
        Assertions.assertTrue(path.getText().contains(time9));
        Assertions.assertTrue(path.getText().contains(time10));
    }

}
