package dev.ksan.etfoglasiserver.controller;

import com.fasterxml.jackson.databind.ObjectMapper;

import dev.ksan.etfoglasiserver.config.SecurityConfig;
import dev.ksan.etfoglasiserver.model.Entry;
import dev.ksan.etfoglasiserver.service.EntryService;
import dev.ksan.etfoglasiserver.service.JWTService;
import dev.ksan.etfoglasiserver.service.MyUserDetailsService;
import dev.ksan.etfoglasiserver.util.SubjectLoader;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.PageImpl;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;


import java.util.List;
import java.util.UUID;

import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(EntryController.class)
@Import(SecurityConfig.class)
class EntryControllerTest {

    @Autowired
    MockMvc mvc;

    @Autowired
    ObjectMapper objectMapper;

    @MockitoBean
    EntryService entryService;

    @MockitoBean
    JWTService jwtService;


    @MockitoBean
    MyUserDetailsService userDetailsService;

    @MockitoBean
    SubjectLoader subjectLoader;


    @Test
    void getEntries_noParams_returns200() throws Exception {
        when(entryService.getEntries(null, null, 0))
                .thenReturn(new PageImpl<>(List.of()));

        mvc.perform(get("/api/entries"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.content").isArray());
    }

    @Test
    void getEntries_withSubjectId_parsesUUIDCorrectly() throws Exception {
        UUID subjectId = UUID.randomUUID();
        when(entryService.getEntries(eq(subjectId), isNull(), eq(0)))
                .thenReturn(new PageImpl<>(List.of()));

        mvc.perform(get("/api/entries")
                        .param("subjectId", subjectId.toString())
                        .param("page", "0"))
                .andExpect(status().isOk());

        verify(entryService).getEntries(eq(subjectId), isNull(), eq(0));
    }

    @Test
    void getEntries_withGroupName_passesGroupNameToService() throws Exception {
        when(entryService.getEntries(isNull(), eq("Physics"), eq(0)))
                .thenReturn(new PageImpl<>(List.of()));

        mvc.perform(get("/api/entries").param("groupName", "Physics"))
                .andExpect(status().isOk());

        verify(entryService).getEntries(isNull(), eq("Physics"), eq(0));
    }

    @Test
    void getEntries_invalidUUID_returns400() throws Exception {
        mvc.perform(get("/api/entries").param("subjectId", "not-a-uuid"))
                .andExpect(status().isBadRequest());
    }


    @Test
    void getGroups_returnsList() throws Exception {
        when(entryService.getAllGroups()).thenReturn(List.of("Math", "Physics", "CS"));

        mvc.perform(get("/api/groups"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.length()").value(3))
                .andExpect(jsonPath("$[0]").value("Math"));
    }

    @Test
    void getGroups_empty_returns200WithEmptyArray() throws Exception {
        when(entryService.getAllGroups()).thenReturn(List.of());

        mvc.perform(get("/api/groups"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.length()").value(0));
    }


    @Test
    @WithMockUser
    void getEntry_existingId_returns200() throws Exception {
        Entry entry = new Entry();
        entry.setTitle("Test Entry");

        when(entryService.getEntryById("entry-1")).thenReturn(entry);

        mvc.perform(get("/api/entries/entry-1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.title").value("Test Entry"));
    }

    @Test
    void getEntry_unauthenticated_returns401or403() throws Exception {
        mvc.perform(get("/api/entries/entry-1"))
                .andExpect(status().is(anyOf(equalTo(401), equalTo(403))));
    }


    //we removed this from controller so test is pointless
//    @Test
//    @WithMockUser
//    void deleteEntry_authenticated_returns200() throws Exception {
//        doNothing().when(entryService).deleteEntry("entry-1");
//
//        mvc.perform(delete("/api/entries/entry-1"))
//                .andExpect(status().isOk());
//    }

    @Test
    void deleteEntry_unauthenticated_returns401or403() throws Exception {
        mvc.perform(delete("/api/entries/entry-1"))
                .andExpect(status().is(anyOf(equalTo(401), equalTo(403))));
    }
}