package org.raddatz.familienarchiv.dashboard;

import org.junit.jupiter.api.Test;
import org.raddatz.familienarchiv.audit.AuditKind;
import org.raddatz.familienarchiv.security.SecurityConfig;
import org.raddatz.familienarchiv.user.AppUser;
import org.raddatz.familienarchiv.security.PermissionAspect;
import org.raddatz.familienarchiv.user.CustomUserDetailsService;
import org.raddatz.familienarchiv.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.context.annotation.Import;
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.Set;
import java.util.UUID;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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(DashboardController.class)
@Import({SecurityConfig.class, PermissionAspect.class, AopAutoConfiguration.class})
class DashboardControllerTest {

    @Autowired MockMvc mockMvc;

    @MockitoBean DashboardService dashboardService;
    @MockitoBean UserService userService;
    @MockitoBean CustomUserDetailsService customUserDetailsService;

    // ─── Security ────────────────────────────────────────────────────────────────

    @Test
    void resume_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(get("/api/dashboard/resume"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser
    void resume_returns403_whenUserHasNoPermissions() throws Exception {
        mockMvc.perform(get("/api/dashboard/resume"))
                .andExpect(status().isForbidden());
    }

    @Test
    void pulse_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(get("/api/dashboard/pulse"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser
    void pulse_returns403_whenUserHasNoPermissions() throws Exception {
        mockMvc.perform(get("/api/dashboard/pulse"))
                .andExpect(status().isForbidden());
    }

    // ─── GET /api/dashboard/resume ────────────────────────────────────────────

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void resume_returnsNull_whenNoInProgressTranscription() throws Exception {
        UUID userId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());
        when(dashboardService.getResume(userId)).thenReturn(null);

        mockMvc.perform(get("/api/dashboard/resume"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").doesNotExist());
    }

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void resume_returnsDocument_whenUserHasInProgressTranscription() throws Exception {
        UUID userId = UUID.randomUUID();
        UUID docId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());

        DashboardResumeDTO resume = new DashboardResumeDTO(docId, "Brief an Frieda",
                "Frieda an Wilhelm · 1923", "Liebe Frieda…", 4, 38, null, List.of());
        when(dashboardService.getResume(userId)).thenReturn(resume);

        mockMvc.perform(get("/api/dashboard/resume"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.documentId").value(docId.toString()))
                .andExpect(jsonPath("$.pct").value(38));
    }

    // ─── GET /api/dashboard/pulse ─────────────────────────────────────────────

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void pulse_returnsWeekStats() throws Exception {
        UUID userId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());

        DashboardPulseDTO pulse = new DashboardPulseDTO(86, 23, 9, 47, 4, List.of());
        when(dashboardService.getPulse(userId)).thenReturn(pulse);

        mockMvc.perform(get("/api/dashboard/pulse"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.pages").value(86))
                .andExpect(jsonPath("$.annotated").value(23));
    }

    @Test
    void activity_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(get("/api/dashboard/activity"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser
    void activity_returns403_whenUserHasNoPermissions() throws Exception {
        mockMvc.perform(get("/api/dashboard/activity"))
                .andExpect(status().isForbidden());
    }

    // ─── GET /api/dashboard/activity ─────────────────────────────────────────

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void activity_returnsDeduplicated_feed() throws Exception {
        UUID userId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());
        when(dashboardService.getActivity(any(UUID.class), anyInt(), any())).thenReturn(List.of());

        mockMvc.perform(get("/api/dashboard/activity"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").isArray());
    }

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void activity_clamps_limit_to_40() throws Exception {
        UUID userId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());
        when(dashboardService.getActivity(any(UUID.class), anyInt(), any())).thenReturn(List.of());

        mockMvc.perform(get("/api/dashboard/activity").param("limit", "9999"))
                .andExpect(status().isOk());

        verify(dashboardService).getActivity(any(UUID.class), org.mockito.ArgumentMatchers.eq(40), any());
    }

    // ─── GET /api/dashboard/activity — kinds param ───────────────────────────

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void activity_parsesKinds_fromCsvQueryParam() throws Exception {
        UUID userId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());
        when(dashboardService.getActivity(any(UUID.class), anyInt(), any())).thenReturn(List.of());

        mockMvc.perform(get("/api/dashboard/activity")
                        .param("kinds", "FILE_UPLOADED", "TEXT_SAVED"))
                .andExpect(status().isOk());

        verify(dashboardService).getActivity(any(UUID.class), anyInt(),
                org.mockito.ArgumentMatchers.eq(Set.of(AuditKind.FILE_UPLOADED, AuditKind.TEXT_SAVED)));
    }

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void activity_returns400_forUnknownKindValue() throws Exception {
        mockMvc.perform(get("/api/dashboard/activity").param("kinds", "INVALID_KIND"))
                .andExpect(status().isBadRequest());
    }

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void activity_defaults_to_rollupEligible_whenKindsAbsent() throws Exception {
        UUID userId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());
        when(dashboardService.getActivity(any(UUID.class), anyInt(), any())).thenReturn(List.of());

        mockMvc.perform(get("/api/dashboard/activity"))
                .andExpect(status().isOk());

        verify(dashboardService).getActivity(any(UUID.class), anyInt(),
                org.mockito.ArgumentMatchers.eq(AuditKind.ROLLUP_ELIGIBLE));
    }

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void activity_treats_single_valid_kind_as_filter() throws Exception {
        UUID userId = UUID.randomUUID();
        when(userService.findByEmail(any())).thenReturn(
                AppUser.builder().id(userId).email("u@test.com").password("pw").build());
        when(dashboardService.getActivity(any(UUID.class), anyInt(), any())).thenReturn(List.of());

        mockMvc.perform(get("/api/dashboard/activity").param("kinds", "COMMENT_ADDED"))
                .andExpect(status().isOk());

        verify(dashboardService).getActivity(any(UUID.class), anyInt(),
                org.mockito.ArgumentMatchers.eq(Set.of(AuditKind.COMMENT_ADDED)));
    }
}
