/*
 * Queue - A Queueing system that can be used to handle labs in higher education
 * Copyright (C) 2016-2024  Delft University of Technology
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package nl.tudelft.queue.controller;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.util.Arrays;
import java.util.List;

import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;

import jakarta.transaction.Transactional;
import nl.tudelft.labracore.api.RoleControllerApi;
import nl.tudelft.labracore.api.dto.EditionCollectionDetailsDTO;
import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
import nl.tudelft.queue.dto.create.QueueEditionCollectionCreateDTO;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import test.TestDatabaseLoader;
import test.labracore.EditionApiMocker;
import test.labracore.EditionCollectionApiMocker;
import test.labracore.SessionApiMocker;
import test.test.TestQueueApplication;

@Transactional
@AutoConfigureMockMvc
@SpringBootTest(classes = TestQueueApplication.class)
public class SharedEditionControllerTest {

	@Autowired
	private TestDatabaseLoader db;

	@Autowired
	private MockMvc mvc;

	@Autowired
	private EditionApiMocker eApi;

	@Autowired
	private EditionCollectionApiMocker edApi;

	@Autowired
	private RoleControllerApi rApi;

	@Autowired
	private SessionApiMocker sApi;
	private EditionCollectionDetailsDTO ec1;

	private EditionDetailsDTO ed1;
	private EditionDetailsDTO ed2;

	@BeforeEach
	void setup() {
		ec1 = db.getRlOopNowEc();
		edApi.mock();
		eApi.mock();
		sApi.mock();

		ed1 = db.getOopNow();
		ed2 = db.getRlNow();
		when(eApi.getEApi().getEditionParticipants(any())).thenReturn(Flux.empty());
		when(edApi.getEcApi().addEditionCollection(any())).thenReturn(Mono.just(1L));
	}

	@Test
	@WithUserDetails("admin")
	void getSharedEdition() throws Exception {
		when(rApi.getRolesByEditions(anySet())).thenReturn(Flux.just());
		mvc.perform(get("/shared-edition/" + ec1.getId()))
				.andExpect(status().is2xxSuccessful())
				.andExpect(view().name("shared-edition/view"))
				.andExpect(model().attributeExists("collection", "editions", "roles", "currentSessions",
						"upComingSessions", "pastSessions"));
	}

	@Test
	@WithUserDetails("teacher1")
	void createSharedEdition() throws Exception {
		var collection = QueueEditionCollectionCreateDTO.builder().editions(List.of(ed1.getId(),
				ed2.getId())).name("my new edition").build();
		mvc.perform(post("/shared-edition/create").with(csrf())
				.contentType(MediaType.APPLICATION_FORM_URLENCODED)
				.content(EntityUtils.toString(new UrlEncodedFormEntity(
						Arrays.asList(
								new BasicNameValuePair("name", "my new edition"),
								new BasicNameValuePair("editions",
										"5, 6")))))

		)
				.andExpect(redirectedUrl("/shared-edition/1"))
				.andExpect(status().is3xxRedirection());
	}

	@Test
	@WithUserDetails("teacher1")
	void createSharedEditionValidated() throws Exception {
		var collection = QueueEditionCollectionCreateDTO.builder().name("not working").build();

		mvc.perform(post("/shared-edition/create").with(csrf()).flashAttr("sharedEdition", collection))
				.andExpect(status().is4xxClientError());
	}

	@ParameterizedTest
	@MethodSource(value = "protectedEndpoints")
	void requestWithoutUserDetailsGoesToLogin(MockHttpServletRequestBuilder request) throws Exception {
		mvc.perform(request.with(csrf()))
				.andExpect(status().is3xxRedirection())
				.andExpect(redirectedUrl("http://localhost/login"));
	}

	@Test
	@WithUserDetails("student1")
	void studentsDoNotHaveAccessToCreate() throws Exception {
		mvc.perform(post("/shared-edition/create").with(csrf()))
				.andExpect(status().is4xxClientError());
	}

	private static List<MockHttpServletRequestBuilder> protectedEndpoints() {
		return List.of(post("/shared-edition/create"), get("/shared-edition/1"));
	}
}
