package com.iqser.red.service.peristence.v1.server.integration.tests;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;

import java.time.OffsetDateTime;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;

import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController;
import com.iqser.red.persistence.service.v2.external.api.impl.controller.DownloadControllerV2;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.model.User;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RemoveDownloadRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatusValue;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;

public class DownloadControllerV2Test {

    public static final String USER_ID = "user-111";
    public static final String DOWNLOAD_ID = "download-123";

    @InjectMocks
    private DownloadControllerV2 downloadControllerV2;

    @Mock
    private DownloadController downloadController;
    @Mock
    private DownloadStatusPersistenceService downloadStatusPersistenceService;
    @Mock
    private UserService userService;

    @Mock
    private DownloadStatusEntity mockDownloadStatusEntity;


    @BeforeEach
    void setUp() {

        openMocks(this);
    }


    @Test
    void shouldThrowNotAllowedExceptionIfUserHasInvalidRoles_getDownloadStatusList() {

        User invalidUser = new User();
        invalidUser.setUserId(USER_ID);
        invalidUser.setRoles(Set.of("RED_ADMIN"));

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(invalidUser));

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            assertThrows(NotAllowedException.class, () -> downloadControllerV2.getDownloadStatusList());
        }
    }


    @Test
    void shouldNotThrowExceptionIfUserHasValidRoles_getDownloadStatusList() {

        User validUser = new User();
        validUser.setUserId(USER_ID);
        validUser.setRoles(Set.of("RED_MANAGER"));

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(validUser));

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            when(downloadStatusPersistenceService.getStatusesByUser(USER_ID)).thenReturn(Collections.emptyList());

            assertDoesNotThrow(() -> downloadControllerV2.getDownloadStatusList());
        }
    }


    @Test
    void shouldThrowNotAllowedExceptionIfUserHasInvalidRoles_getDownloadStatus() {

        User invalidUser = new User();
        invalidUser.setUserId(USER_ID);
        invalidUser.setRoles(Collections.emptySet());

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(invalidUser));

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            assertThrows(NotAllowedException.class, () -> downloadControllerV2.getDownloadStatus(DOWNLOAD_ID));
        }
    }


    @Test
    void shouldNotThrowExceptionIfUserHasValidRoles_getDownloadStatus() {

        User validUser = new User();
        validUser.setUserId(USER_ID);
        validUser.setRoles(Set.of("RED_USER"));

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(validUser));

        when(downloadStatusPersistenceService.getStatusesByUuid(DOWNLOAD_ID)).thenReturn(mockDownloadStatusEntity);

        when(mockDownloadStatusEntity.getUuid()).thenReturn(DOWNLOAD_ID);
        when(mockDownloadStatusEntity.getUserId()).thenReturn(USER_ID);
        when(mockDownloadStatusEntity.getFilename()).thenReturn("dummy-file");
        when(mockDownloadStatusEntity.getMimeType()).thenReturn("application/pdf");
        when(mockDownloadStatusEntity.getErrorCause()).thenReturn(null);
        when(mockDownloadStatusEntity.getStatus()).thenReturn(DownloadStatusValue.READY);
        when(mockDownloadStatusEntity.getCreationDate()).thenReturn(OffsetDateTime.now());
        when(mockDownloadStatusEntity.getLastDownload()).thenReturn(OffsetDateTime.now());
        when(mockDownloadStatusEntity.getFileSize()).thenReturn(12345L);
        DossierEntity dossierEntity = mock(DossierEntity.class);
        when(dossierEntity.getId()).thenReturn("dossier-123");
        when(mockDownloadStatusEntity.getDossier()).thenReturn(dossierEntity);
        when(mockDownloadStatusEntity.getFiles()).thenReturn(Collections.emptyList());
        when(mockDownloadStatusEntity.getDownloadFileTypes()).thenReturn(Collections.emptySet());
        when(mockDownloadStatusEntity.getReports()).thenReturn(Collections.emptyList());

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            assertDoesNotThrow(() -> downloadControllerV2.getDownloadStatus(DOWNLOAD_ID));
        }
    }


    @Test
    void shouldThrowNotAllowedExceptionIfUserHasInvalidRoles_deleteDownload() {

        User invalidUser = new User();
        invalidUser.setUserId(USER_ID);
        invalidUser.setRoles(Set.of("RED_USER_ADMIN"));

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(invalidUser));

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            assertThrows(NotAllowedException.class, () -> downloadControllerV2.deleteDownload(DOWNLOAD_ID));
        }
    }


    @Test
    void shouldNotThrowExceptionIfUserHasValidRoles_deleteDownload() {

        User validUser = new User();
        validUser.setUserId(USER_ID);
        validUser.setRoles(Set.of("RED_MANAGER"));

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(validUser));

        when(downloadStatusPersistenceService.getStatusesByUuid(DOWNLOAD_ID)).thenReturn(mockDownloadStatusEntity);
        when(mockDownloadStatusEntity.getStorageId()).thenReturn("storage-123");

        doNothing().when(downloadController).deleteDownloadStatus(any(RemoveDownloadRequest.class));

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            assertDoesNotThrow(() -> downloadControllerV2.deleteDownload(DOWNLOAD_ID));

            verify(downloadController).deleteDownloadStatus(any(RemoveDownloadRequest.class));
        }
    }


    @Test
    void shouldThrowNotAllowedExceptionIfUserHasInvalidRoles_download() {

        User invalidUser = new User();
        invalidUser.setUserId(USER_ID);
        invalidUser.setRoles(Set.of("NON_ELIGIBLE_ROLE"));

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(invalidUser));

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            assertThrows(NotAllowedException.class, () -> downloadControllerV2.download(DOWNLOAD_ID));
        }
    }


    @Test
    void shouldNotThrowExceptionIfUserHasValidRoles_download() {

        User validUser = new User();
        validUser.setUserId(USER_ID);
        validUser.setRoles(Set.of("RED_USER"));

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(validUser));

        when(downloadStatusPersistenceService.getStatusesByUuid(DOWNLOAD_ID)).thenReturn(mockDownloadStatusEntity);
        when(mockDownloadStatusEntity.getStorageId()).thenReturn("storage-123");

        doNothing().when(downloadController).downloadFile("storage-123");

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);

            assertDoesNotThrow(() -> downloadControllerV2.download(DOWNLOAD_ID));

            verify(downloadController).downloadFile("storage-123");
        }
    }

}
