package de.uni_hamburg.fs;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class FieldFeatureTest {

    @Test
    void testGetJavaClass() {
        // given
        Field mockField = mock(Field.class);
        doReturn(String.class).when(mockField).getType();
        FieldFeature fieldFeature = new FieldFeature(mockField);

        // when
        Class<?> result = fieldFeature.getJavaClass();

        // then
        assertThat(result).isEqualTo(String.class);
    }

    @Test
    void testGetObjectValue() throws IllegalAccessException {
        // given
        Field mockField = mock(Field.class);
        Object testObject = new Object();
        Object expectedValue = "testValue";
        when(mockField.get(testObject)).thenReturn(expectedValue);
        FieldFeature fieldFeature = new FieldFeature(mockField);

        // when
        Object result = fieldFeature.getObjectValue(testObject);

        // then
        assertThat(result).isEqualTo(expectedValue);
    }

    @Test
    void testGetObjectValueWithException() throws IllegalAccessException {
        // given
        Field mockField = mock(Field.class);
        Object testObject = new Object();
        when(mockField.get(testObject)).thenThrow(new IllegalAccessException("Test exception"));
        FieldFeature fieldFeature = new FieldFeature(mockField);

        // when & then
        assertThatThrownBy(() -> fieldFeature.getObjectValue(testObject))
            .isInstanceOf(RuntimeException.class)
            .hasMessageContaining("Exception during feature setting:");
    }

    @Test
    void testCanSetWhenNotFinal() {
        // given
        Field mockField = mock(Field.class);
        when(mockField.getModifiers()).thenReturn(Modifier.PUBLIC);
        FieldFeature fieldFeature = new FieldFeature(mockField);

        // when
        boolean result = fieldFeature.canSet();

        // then
        assertThat(result).isTrue();
    }

    @Test
    void testCanSetWhenFinal() {
        // given
        Field mockField = mock(Field.class);
        when(mockField.getModifiers()).thenReturn(Modifier.FINAL);
        FieldFeature fieldFeature = new FieldFeature(mockField);

        // when
        boolean result = fieldFeature.canSet();

        // then
        assertThat(result).isFalse();
    }

    @Test
    void testSetObjectValue() throws IllegalAccessException {
        // given
        Field mockField = mock(Field.class);
        Object testObject = new Object();
        String value = "newValue";
        doNothing().when(mockField).set(testObject, value);
        FieldFeature fieldFeature = new FieldFeature(mockField);

        // when
        fieldFeature.setObjectValue(testObject, value);

        // then
        verify(mockField).set(testObject, value);
    }

    @Test
    void testSetObjectValueWithException() throws IllegalAccessException {
        // given
        Field mockField = mock(Field.class);
        Object testObject = new Object();
        String value = "newValue";
        doThrow(new IllegalAccessException("Test exception")).when(mockField)
            .set(testObject, value);
        FieldFeature fieldFeature = new FieldFeature(mockField);

        // when & then
        assertThatThrownBy(() -> fieldFeature.setObjectValue(testObject, value))
            .isInstanceOf(RuntimeException.class)
            .hasMessageContaining("Exception during feature setting:");
    }
}