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.mockStatic;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;

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.StatusController;
import com.iqser.red.persistence.service.v2.external.api.impl.controller.ComponentControllerV2;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.CurrentApplicationTypeProvider;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
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.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;

import lombok.SneakyThrows;

public class ComponentControllerV2Test {

    public static final String USER_ID = "user-111";
    public static final String FILE_ID = "file-789";
    public static final String DOSSIER_ID = "dossier-456";
    public static final String DOSSIER_TEMPLATE_ID = "template-123";

    @InjectMocks
    private ComponentControllerV2 componentControllerV2;

    @Mock
    private UserService userService;
    @Mock
    private ComponentLogService componentLogService;
    @Mock
    private FileStatusService fileStatusService;
    @Mock
    private DossierTemplatePersistenceService dossierTemplatePersistenceService;
    @Mock
    private StatusController statusController;
    @Mock
    private ComponentLog mockComponentLog;
    @Mock
    private CurrentApplicationTypeProvider currentApplicationTypeProvider;
    @Mock
    private AccessControlService accessControlService;


    @BeforeEach
    @SneakyThrows
    void setUp() {

        openMocks(this);
        when(currentApplicationTypeProvider.isDocuMine()).thenReturn(true);
    }


    @Test
    void shouldThrowNotAllowedExceptionIfUserHasInvalidRoles_getComponents() {

        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, () -> componentControllerV2.getComponents(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID, false));
        }
    }


    @Test
    void shouldNotThrowNotAllowedExceptionIfUserHasValidRoles_getComponents() {

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

        when(componentLogService.getComponentLog(DOSSIER_ID, FILE_ID)).thenReturn(mockComponentLog);
        when(mockComponentLog.getComponentLogEntries()).thenReturn(Collections.emptyList());
        when(fileStatusService.getFileName(FILE_ID)).thenReturn("mock-file-name");

        try (MockedStatic<KeycloakSecurity> keycloakSecurityMock = mockStatic(KeycloakSecurity.class)) {
            keycloakSecurityMock.when(KeycloakSecurity::getUserId).thenReturn(USER_ID);
            when(userService.getUserById(USER_ID)).thenReturn(Optional.of(validUser));

            assertDoesNotThrow(() -> componentControllerV2.getComponents(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID, false));
        }
    }


    @Test
    void shouldThrowNotAllowedExceptionIfApplicationTypeIsRM_getComponents() {

        when(currentApplicationTypeProvider.isDocuMine()).thenReturn(false);

        when(userService.getUserById(USER_ID)).thenReturn(Optional.of(new User()));

        assertThrows(NotAllowedException.class, () -> componentControllerV2.getComponents(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID, false));
    }


    @Test
    void shouldThrowNotAllowedExceptionIfApplicationTypeIsRM_getComponentsOfDossier() {

        when(currentApplicationTypeProvider.isDocuMine()).thenReturn(false);

        assertThrows(NotAllowedException.class, () -> componentControllerV2.getComponentsOfDossier(DOSSIER_TEMPLATE_ID, DOSSIER_ID, false));
    }


    @Test
    void shouldNotThrowExceptionIfApplicationTypeIsValid_getComponentsOfDossier() {

        FileStatus fileStatus = FileStatus.builder().fileId(FILE_ID).build();
        when(statusController.getDossierStatus(DOSSIER_ID)).thenReturn(Collections.singletonList(fileStatus));

        when(componentLogService.getComponentLog(DOSSIER_ID, FILE_ID)).thenReturn(mockComponentLog);
        when(fileStatusService.getFileName(FILE_ID)).thenReturn("mock-file-name");
        when(mockComponentLog.getComponentLogEntries()).thenReturn(Collections.emptyList());

        assertDoesNotThrow(() -> componentControllerV2.getComponentsOfDossier(DOSSIER_TEMPLATE_ID, DOSSIER_ID, false));
    }


    @Test
    void shouldThrowNotAllowedExceptionIfApplicationTypeIsRM_addOverride() {

        when(currentApplicationTypeProvider.isDocuMine()).thenReturn(false);

        Component component = Component.builder().name("dummy").componentValues(Collections.emptyList()).build();

        assertThrows(NotAllowedException.class, () -> componentControllerV2.addOverride(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID, component));
    }


    @Test
    void shouldNotThrowException_addOverride() {

        Component component = Component.builder().name("dummy").componentValues(Collections.emptyList()).build();

        assertDoesNotThrow(() -> componentControllerV2.addOverride(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID, component));
    }


    @Test
    void shouldThrowNotAllowedExceptionIfApplicationTypeIsRM_getOverrides() {

        when(currentApplicationTypeProvider.isDocuMine()).thenReturn(false);

        assertThrows(NotAllowedException.class, () -> componentControllerV2.getOverrides(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID));
    }


    @Test
    void shouldNotThrowException_getOverrides() {

        FileModel fileModel = new FileModel();
        fileModel.setId(FILE_ID);
        when(fileStatusService.getStatus(FILE_ID)).thenReturn(fileModel);

        when(componentLogService.getComponentLog(DOSSIER_ID, FILE_ID)).thenReturn(mockComponentLog);
        ComponentLogEntry overriddenEntry = new ComponentLogEntry();
        overriddenEntry.setName("dummy");
        overriddenEntry.setValues(Collections.emptyList());
        overriddenEntry.setOverridden(true);
        when(mockComponentLog.getComponentLogEntries()).thenReturn(Collections.singletonList(overriddenEntry));

        assertDoesNotThrow(() -> componentControllerV2.getOverrides(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID));
    }


    @Test
    void shouldThrowNotAllowedExceptionIfApplicationTypeIsRM_revertOverrides() {

        when(currentApplicationTypeProvider.isDocuMine()).thenReturn(false);

        RevertOverrideRequest revertOverrideRequest = new RevertOverrideRequest();

        assertThrows(NotAllowedException.class, () -> componentControllerV2.revertOverrides(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID, revertOverrideRequest));
    }


    @Test
    void shouldNotThrowException_revertOverrides() {

        RevertOverrideRequest revertOverrideRequest = new RevertOverrideRequest();

        assertDoesNotThrow(() -> componentControllerV2.revertOverrides(DOSSIER_TEMPLATE_ID, DOSSIER_ID, FILE_ID, revertOverrideRequest));
    }

}
