/*
 * 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.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
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.result.MockMvcResultMatchers.*;
import static test.JsonContentMatcher.jsonContent;

import java.util.List;

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.queue.dto.view.statistics.AssistantSessionStatisticsViewDto;
import nl.tudelft.queue.dto.view.statistics.session.GeneralSessionStatisticsViewDto;
import nl.tudelft.queue.model.labs.RegularLab;
import nl.tudelft.queue.repository.LabRepository;
import test.TestDatabaseLoader;
import test.test.TestQueueApplication;

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

	@Autowired
	private TestDatabaseLoader db;

	@Autowired
	private MockMvc mvc;

	@Autowired
	private LabRepository labRepository;

	RegularLab oopNowRegLab1;

	RegularLab rlOopNowSharedLab;

	@BeforeEach
	void init() {
		db.mockAll();

		oopNowRegLab1 = db.getOopNowRegularLab1();
		rlOopNowSharedLab = db.getRlOopNowSharedLab();
	}

	@Test
	@WithUserDetails("admin")
	void regularLabReturnsAnythingWithoutEditions() throws Exception {
		mvc.perform(get("/lab/" + oopNowRegLab1.getId() + "/status/assistant/freq"))
				.andExpect(status().is2xxSuccessful())
				.andExpect(content().contentType(MediaType.APPLICATION_JSON))
				.andExpect(jsonContent(AssistantSessionStatisticsViewDto.class)
						.test(dto -> assertThat(dto.getAssistantNames()).isNotEmpty()));
	}

	@Test
	@WithUserDetails("admin")
	void filterWorks() throws Exception {
		mvc.perform(get("/lab/" + oopNowRegLab1.getId() + "/status/assistant/freq").queryParam("editions",
				"23423432432324", "23432432432"))
				.andExpect(status().is2xxSuccessful())
				.andExpect(content().contentType(MediaType.APPLICATION_JSON))
				.andExpect(jsonContent(AssistantSessionStatisticsViewDto.class)
						.test(dto -> assertThat(dto.getAssistantNames()).isEmpty()));
	}

	@Test
	@WithUserDetails("admin")
	void testGeneralStatisticsEndpoint() throws Exception {
		mvc.perform(get("/lab/{labId}/status/general", oopNowRegLab1.getId()))
				.andExpect(status().isOk())
				.andExpect(content().contentType(MediaType.APPLICATION_JSON))
				.andExpect(jsonContent(GeneralSessionStatisticsViewDto.class)
						.test(dto -> assertThat(dto.getRequestCounts().questionCount()
								+ dto.getRequestCounts().submissionCount())
								.isEqualTo(oopNowRegLab1.getRequests().size())));

	}

	@Test
	@WithUserDetails("admin")
	void testAssignmentCountEndpoint() throws Exception {
		mvc.perform(get("/lab/{labId}/status/assignment/freq", oopNowRegLab1.getId()))
				.andExpect(status().is2xxSuccessful())
				.andExpect(content().contentType(MediaType.APPLICATION_JSON))
				.andExpect(jsonContent(List.class)
						.test(list -> assertThat(list).hasSize(1)));
	}

	@Test
	@WithUserDetails("admin")
	void testRequestDistributionEndpoint() throws Exception {
		mvc.perform(get("/lab/{labId}/status/request/distribution?nMinutes=3", oopNowRegLab1.getId()))
				.andExpect(status().is2xxSuccessful())
				.andExpect(content().contentType(MediaType.APPLICATION_JSON))
				.andExpect(jsonContent(List.class)
						.test(list -> assertThat(list).hasSize(1)));
	}

	@Test
	@WithUserDetails("admin")
	void testBuildingDistributionEndpoint() throws Exception {
		mvc.perform(get("/lab/{labId}/status/building/distribution?nMinutes=3", oopNowRegLab1.getId()))
				.andExpect(status().is2xxSuccessful())
				.andExpect(content().contentType(MediaType.APPLICATION_JSON))
				.andExpect(jsonContent(List.class)
						.test(list -> assertThat(list).hasSize(1)));
	}

	@Test
	@WithUserDetails("admin")
	void testTimeDistribution() throws Exception {
		mvc.perform(get("/lab/{labId}/status/time/distribution?nMinutes=3", oopNowRegLab1.getId()))
				.andExpect(status().is2xxSuccessful())
				.andExpect(content().contentType(MediaType.APPLICATION_JSON))
				.andExpect(jsonContent(List.class)
						.test(list -> assertThat(list).hasSize(1)));
	}

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

	private static List<MockHttpServletRequestBuilder> protectedEndpoints() {
		return List.of(
				get("/lab/1/status/general"),
				get("/lab/1/status/assistant/freq"),
				get("/lab/1/status/assignment/freq"),
				get("/lab/1/status/request/distribution"),
				get("/lab/1/status/building/distribution"),
				get("/lab/1/status/time/distribution"));
	}

}
