package org.raddatz.familienarchiv.user;

import org.junit.jupiter.api.Test;
import org.raddatz.familienarchiv.security.SecurityConfig;
import org.raddatz.familienarchiv.document.Document;
import org.raddatz.familienarchiv.security.PermissionAspect;
import org.raddatz.familienarchiv.user.CustomUserDetailsService;
import org.raddatz.familienarchiv.document.DocumentService;
import org.raddatz.familienarchiv.document.DocumentVersionService;
import org.raddatz.familienarchiv.importing.CanonicalImportOrchestrator;
import org.raddatz.familienarchiv.importing.ImportStatus;
import org.raddatz.familienarchiv.document.ThumbnailBackfillService;
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.time.LocalDateTime;
import java.util.List;

import static org.mockito.ArgumentMatchers.anyList;
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.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;

@WebMvcTest(AdminController.class)
@Import({SecurityConfig.class, PermissionAspect.class, AopAutoConfiguration.class})
class AdminControllerTest {

    @Autowired MockMvc mockMvc;

    @MockitoBean CanonicalImportOrchestrator importOrchestrator;
    @MockitoBean DocumentService documentService;
    @MockitoBean DocumentVersionService documentVersionService;
    @MockitoBean ThumbnailBackfillService thumbnailBackfillService;
    @MockitoBean CustomUserDetailsService customUserDetailsService;

    // ─── GET /api/admin/import-status ─────────────────────────────────────────

    @Test
    @WithMockUser(authorities = "ADMIN")
    void importStatus_returns200_withStatusCode_whenAdmin() throws Exception {
        ImportStatus status = new ImportStatus(
                ImportStatus.State.IDLE, "IMPORT_IDLE", "Kein Import gestartet.", 0, List.of(), null);
        when(importOrchestrator.getStatus()).thenReturn(status);

        mockMvc.perform(get("/api/admin/import-status"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.state").value("IDLE"))
                .andExpect(jsonPath("$.statusCode").value("IMPORT_IDLE"))
                .andExpect(jsonPath("$.processed").value(0));
    }

    @Test
    @WithMockUser(authorities = "ADMIN")
    void importStatus_messageField_notPresentInApiResponse() throws Exception {
        ImportStatus status = new ImportStatus(
                ImportStatus.State.IDLE, "IMPORT_IDLE", "Kein Import gestartet.", 0, List.of(), null);
        when(importOrchestrator.getStatus()).thenReturn(status);

        mockMvc.perform(get("/api/admin/import-status"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.message").doesNotExist());
    }

    @Test
    void importStatus_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(get("/api/admin/import-status"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(authorities = "READ_ALL")
    void importStatus_returns403_whenUserLacksAdminPermission() throws Exception {
        mockMvc.perform(get("/api/admin/import-status"))
                .andExpect(status().isForbidden());
    }

    @Test
    void backfillVersions_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(post("/api/admin/backfill-versions").with(csrf()))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(roles = "USER")
    void backfillVersions_returns403_whenNotAdmin() throws Exception {
        mockMvc.perform(post("/api/admin/backfill-versions").with(csrf()))
                .andExpect(status().isForbidden());
    }

    @Test
    @WithMockUser(authorities = "ADMIN")
    void backfillVersions_returns200_withCount_whenAdmin() throws Exception {
        when(documentService.getDocumentsWithoutVersions()).thenReturn(List.of(Document.builder().build()));
        when(documentVersionService.backfillMissingVersions(anyList())).thenReturn(1);

        mockMvc.perform(post("/api/admin/backfill-versions").with(csrf()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.count").value(1));
    }

    // ─── POST /api/admin/backfill-file-hashes ──────────────────────────────────

    @Test
    void backfillFileHashes_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(post("/api/admin/backfill-file-hashes").with(csrf()))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(roles = "USER")
    void backfillFileHashes_returns403_whenNotAdmin() throws Exception {
        mockMvc.perform(post("/api/admin/backfill-file-hashes").with(csrf()))
                .andExpect(status().isForbidden());
    }

    @Test
    @WithMockUser(authorities = "ADMIN")
    void backfillFileHashes_returns200_withCount_whenAdmin() throws Exception {
        when(documentService.backfillFileHashes()).thenReturn(3);

        mockMvc.perform(post("/api/admin/backfill-file-hashes").with(csrf()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.count").value(3));
    }

    // ─── POST /api/admin/backfill-titles (#726) ────────────────────────────────

    @Test
    void backfillTitles_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(post("/api/admin/backfill-titles").with(csrf()))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(roles = "USER")
    void backfillTitles_returns403_whenNotAdmin() throws Exception {
        mockMvc.perform(post("/api/admin/backfill-titles").with(csrf()))
                .andExpect(status().isForbidden());
    }

    @Test
    @WithMockUser(authorities = "ADMIN")
    void backfillTitles_returns200_withCount_whenAdmin() throws Exception {
        when(documentService.backfillTitles()).thenReturn(7);

        mockMvc.perform(post("/api/admin/backfill-titles").with(csrf()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.count").value(7));
    }

    // ─── POST /api/admin/generate-thumbnails ───────────────────────────────────

    @Test
    void generateThumbnails_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(post("/api/admin/generate-thumbnails").with(csrf()))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(roles = "USER")
    void generateThumbnails_returns403_whenNotAdmin() throws Exception {
        mockMvc.perform(post("/api/admin/generate-thumbnails").with(csrf()))
                .andExpect(status().isForbidden());
    }

    @Test
    @WithMockUser(authorities = "ADMIN")
    void generateThumbnails_returns202_withStatus_whenAdmin() throws Exception {
        ThumbnailBackfillService.BackfillStatus status = new ThumbnailBackfillService.BackfillStatus(
                ThumbnailBackfillService.State.RUNNING, "running…", 10, 0, 0, 0, LocalDateTime.now());
        when(thumbnailBackfillService.getStatus()).thenReturn(status);

        mockMvc.perform(post("/api/admin/generate-thumbnails").with(csrf()))
                .andExpect(status().isAccepted())
                .andExpect(jsonPath("$.state").value("RUNNING"))
                .andExpect(jsonPath("$.total").value(10));

        verify(thumbnailBackfillService).runBackfillAsync();
    }

    // ─── GET /api/admin/thumbnail-status ───────────────────────────────────────

    @Test
    void thumbnailStatus_returns401_whenUnauthenticated() throws Exception {
        mockMvc.perform(get("/api/admin/thumbnail-status"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(authorities = "ADMIN")
    void thumbnailStatus_returns200_withCurrentStatus_whenAdmin() throws Exception {
        ThumbnailBackfillService.BackfillStatus status = new ThumbnailBackfillService.BackfillStatus(
                ThumbnailBackfillService.State.DONE, "Fertig: 5 erzeugt, 0 übersprungen, 0 fehlgeschlagen.",
                5, 5, 0, 0, LocalDateTime.now());
        when(thumbnailBackfillService.getStatus()).thenReturn(status);

        mockMvc.perform(get("/api/admin/thumbnail-status"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.state").value("DONE"))
                .andExpect(jsonPath("$.processed").value(5))
                .andExpect(jsonPath("$.total").value(5));
    }
}
