package Team5.SmartTowns;

import Team5.SmartTowns.data.*;
import Team5.SmartTowns.landmarks.LandmarksController;
import Team5.SmartTowns.placeswithcoordinates.LocationsCoordinates;
import Team5.SmartTowns.placeswithcoordinates.PlacesController;
import Team5.SmartTowns.placeswithcoordinates.PlacesCoordinatesRepository;
import Team5.SmartTowns.placeswithcoordinates.TownWithTrails;
import Team5.SmartTowns.rewards.RewardsRepository;
import Team5.SmartTowns.security.SecurityConfiguration;
import Team5.SmartTowns.towns.TownController;
import Team5.SmartTowns.towns.TownStorage;
import Team5.SmartTowns.towns.Towns;
import jakarta.servlet.ServletException;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static net.bytebuddy.matcher.ElementMatchers.is;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.not;
import static org.hamcrest.Matchers.anything;
import static org.junit.jupiter.api.Assertions.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BooleanSupplier;

import static org.hamcrest.Matchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)

//@ExtendWith(SpringExtension.class)
@WebMvcTest({PlacesController.class, LandmarksController.class, TownController.class})
@Import(SecurityConfiguration.class)
@AutoConfigureTestDatabase //todo, check if this is okay - https://stackoverflow.com/questions/57958507/spring-boot-cannot-find-datasource-bean-in-junit-tests
public class LightweightMockMVCTests {

    //todo check approvalform


    @Autowired
    private MockMvc mockMvc;


    @MockBean
    private PlacesCoordinatesRepository placesRepo;

    @MockBean
    private LocationRepository locationRepo;

    @MockBean
    private TrailsRepository trailsRepo;

    @MockBean
    private RewardsRepository rewardsRepository;

    @MockBean
    private TownRepository townRepo;

    @MockBean
    TownStorage townStore;

    //todo test approval site lest, post as well?


    @Test
    public void homeGreetingTest() throws Exception {
        this.mockMvc.perform(get("/mobile-home")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Welcome to VZTA Smart Towns!")));
    }

//    @Test
//    public void noDBTest() throws Exception {
//        TownWithTrails townTrail = new TownWithTrails("Caerphilly","51.57903","-3.22075",
//                "51.60418", "51.55093", "-3.25222", "-3.17696");
//       Trail trail = new Trail(102L, "Caerphilly Pub Trail","0101" );
//
//        given(this.placesRepo.getAllTownCoords()).willReturn(Arrays.asList(townTrail));
//        given(this.trailsRepo.getAllTrails()).willReturn(Arrays.asList(trail));
//        this.mockMvc.perform(get("/towns/Caerphilly")).andDo(print()).andExpect(status().isOk())
//                .andExpect(content().string(containsString("MockTrail")));
//    }


    @Test
    public void ensureLandmarkFormButtonShowsTest() throws Exception {
//        Towns town = new Towns("MockTown",01,99,67,"null"); //todo this is hard coded, fix.
//
//        given(this.townStore.getTownList()).willReturn(Arrays.asList(town));
//        this.mockMvc.perform(get("/mobile-home")).andExpect(status().isOk()) // check h1
//                .andExpect(content().string(containsString("Welcome to VZTA Smart Towns!")))
//                .andExpect(content().string(containsString("MockTown"+"99")))
//                .andExpect(content().string(containsString("67%")));
        this.mockMvc.perform(get("/mobile-home"))
                .andExpect(content().string(containsString("Submit Landmark!"))); // all visitors can submit
//                .andExpect(content().string(containsString("Review Landmark!")));
        AssertionError thrown = assertThrows(AssertionError.class , () ->{
            this.mockMvc.perform(get("/mobile-home")).andExpect(content().string(containsString("Review Landmark!")));;//review doesn't show for visitors
        });
        assertTrue(thrown.getMessage().contains("Expected: a string containing \"Review Landmark!\""));
    }


    @Test
    public void checkLandmarkSubmissionFormDisplaysFieldsTest() throws Exception {
        this.mockMvc.perform(get("/landmarkSubmission")).andExpect(status().isOk()) // check h1
                .andExpect(content().string(containsString("Interested in joining our trails? Sign up Here!"))) //checks that all form criteria shows correctly
                .andExpect(content().string(containsString("Business Name:"))) //input title for business name
                .andExpect(content().string(containsString("Business name here..."))) //text box content indicator
                .andExpect(content().string(containsString("Contact Address"))) //input title
                .andExpect(content().string(containsString("E-mail here..."))) //text box indicator
                .andExpect(content().string(containsString("Please Describe Your Business:")))//input title
                .andExpect(content().string(containsString("Max 200 words please...")))//text box indicator
                .andExpect(content().string(containsString("Your Location:")))//input title
                .andExpect(content().string(containsString("Select Location")))//drop down default choice
                .andExpect(content().string(containsString("Caerphilly"))) // checking all drop-down options - all hard-coded as of now todo change later?
                .andExpect(content().string(containsString("Risca")))
                .andExpect(content().string(containsString("Penarth")))
                .andExpect(content().string(containsString("Trail:")))//input title
                .andExpect(content().string(containsString("Select Trail")))//drop down default choice
                .andExpect(content().string(containsString("(Caerphilly) Castle Trail"))) // checking all drop-down options
                .andExpect(content().string(containsString("(Caerphilly) Pub Trail")))
                .andExpect(content().string(containsString("(Caerphilly) Heritage Trail")))
                .andExpect(content().string(containsString("(Risca) Heritage and Culture Trail")))
                .andExpect(content().string(containsString("(Penarth) Esplanade Trail")))
                .andExpect(content().string(containsString("Submit")));//button content
    }

    @Test
    public void trailFragmentInitiallyLoadsCorrectlyForValidTrails() throws Exception {
        Trail trail = new Trail(102L, "Mock Caerphilly Trail","0101" );

        given(this.trailsRepo.getAllTrails()).willReturn(Arrays.asList(trail));
        this.mockMvc.perform(get("/trails/Mock-Caerphilly-Trail")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Mock Caerphilly Trail")));

        assertThrows(ServletException.class, () ->{
            this.mockMvc.perform(get("/trails/This-Trail-Doesnt-Exist")).andExpect(status().isNotFound());
        });
    }


    @Test
    public void trailFragmentLoadsMapForValidTrails() throws Exception { //tests trails page (which shows a list of trail-locations)
        Trail trail = new Trail(102L, "Caerphilly Castle Trail","0102" );
        given(this.trailsRepo.getAllTrails()).willReturn(Arrays.asList(trail));

        Location location = new Location(1,"MockLocation", "Mock@Test.co.uk", //mock trail location
                "MockDescription","MockCaerphilly","102",true);
        given(this.locationRepo.getAllApprovedLocations()).willReturn(Arrays.asList(location));

        this.mockMvc.perform(get("/trails/Caerphilly-Castle-Trail")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Caerphilly Castle Trail"))) //tests that trail name loads correctly
                .andExpect(content().string(containsString("MockLocation"))) // checks fake trail-location loads correctly
                .andExpect(content().string(containsString("https://www.google.com/maps/dir/51.57623,-3.21910/51.575372,-3.219186/51.576363,-3.220712//@11z&output=svembed"))) //checks that href works
                .andExpect(content().string(containsString("https://maps.google.com/maps?q=51.57623,-3.21910&output=svembed"))); //checks that current map-substitute shows
    }



    @Test
    public void trailLocationPageTest() throws Exception { //tests individual trail-location page
        Location location = new Location(1,"MockLocation", "Mock@Test.co.uk",
                "MockDescription","MockCaerphilly","99",true);
        given(this.locationRepo.getAllApprovedLocations()).willReturn(Arrays.asList(location));
        LocationsCoordinates locCoords = new LocationsCoordinates(1,51.57756,-3.22002);
//        LocationsCoordinates locCoords2 = new LocationsCoordinates(2,52.00,-3.10);
        given(this.placesRepo.getAllLocationCoords()).willReturn(Arrays.asList(locCoords));


        Trail trail = new Trail(99L, "MockLocation","0099");

        String mock= "MockLocation";
        given(this.trailsRepo.getTrailNameWithID("99")).willReturn(mock);
        this.mockMvc.perform(get("/checkpoints/MockLocation")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("MockLocation"))) //checks mock-lkocation name is showing
                .andExpect(content().string(containsString("MockDescription"))) //checks description shows
                .andExpect(content().string(containsString("MockCaerphilly"))) //checks mock-town shows
                .andExpect(content().string(containsString("https://maps.google.com/maps?q=51.57756,-3.22002&hl=en&z=20&output=embed"))) //check iframe src is embedded correctly
                .andExpect(content().string(containsString("<a href=\"/trails/MockLocation\">Return</a></H2>"))); //checks href leads back to parent trail-list

    }
}