package io.dropwizard.metrics.graphite;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.graphite.Graphite;
import com.codahale.metrics.graphite.GraphiteReporter;
import com.codahale.metrics.graphite.GraphiteUDP;
import io.dropwizard.configuration.YamlConfigurationFactory;
import io.dropwizard.jackson.DiscoverableSubtypeResolver;
import io.dropwizard.jackson.Jackson;
import io.dropwizard.validation.BaseValidator;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

import java.lang.reflect.Field;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

class GraphiteReporterFactoryTest {

    private final GraphiteReporter.Builder builderSpy = mock(GraphiteReporter.Builder.class);

    private final GraphiteReporterFactory graphiteReporterFactory = new GraphiteReporterFactory() {
        @Override
        protected GraphiteReporter.Builder builder(MetricRegistry registry) {
            return builderSpy;
        }
    };

    @Test
    void isDiscoverable() {
        assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes())
            .contains(GraphiteReporterFactory.class);
    }

    @Test
    void createDefaultFactory() throws Exception {
        final GraphiteReporterFactory factory = new YamlConfigurationFactory<>(GraphiteReporterFactory.class,
             BaseValidator.newValidator(), Jackson.newObjectMapper(), "dw")
            .build();
        assertThat(factory.getFrequency()).isNotPresent();
    }

    @Test
    void testNoAddressResolutionForGraphite() throws Exception {
        graphiteReporterFactory.build(new MetricRegistry());

        final ArgumentCaptor<Graphite> argument = ArgumentCaptor.forClass(Graphite.class);
        verify(builderSpy).build(argument.capture());

        final Graphite graphite = argument.getValue();
        assertThat(getField(graphite, "hostname")).isEqualTo("localhost");
        assertThat(getField(graphite, "port")).isEqualTo(2003);
        assertThat(getField(graphite, "address")).isNull();
    }

    @Test
    void testCorrectTransportForGraphiteUDP() throws Exception {
        graphiteReporterFactory.setTransport("udp");
        graphiteReporterFactory.build(new MetricRegistry());

        final ArgumentCaptor<GraphiteUDP> argument = ArgumentCaptor.forClass(GraphiteUDP.class);
        verify(builderSpy).build(argument.capture());

        final GraphiteUDP graphite = argument.getValue();
        assertThat(getField(graphite, "hostname")).isEqualTo("localhost");
        assertThat(getField(graphite, "port")).isEqualTo(2003);
        assertThat(getField(graphite, "address")).isNull();
    }

    private static Object getField(GraphiteUDP graphite, String name) throws NoSuchFieldException {
        try {
            return getInaccessibleField(GraphiteUDP.class, name).get(graphite);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    private static Object getField(Graphite graphite, String name) throws NoSuchFieldException {
        try {
            return getInaccessibleField(Graphite.class, name).get(graphite);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    private static Field getInaccessibleField(Class klass, String name) throws NoSuchFieldException {
        Field field = klass.getDeclaredField(name);
        field.setAccessible(true);
        return field;
    }
}

@@ -1918,38 +1918,143 @@
     
-assertThat(getField(graphite, 
+final FieldAccessor%3CGraphite%3E graphiteFieldAccessor = new FieldAccessor%3C%3E(graphite);%0A        assertThat(graphiteFieldAccessor.getField(
 %22hos
@@ -2106,34 +2106,46 @@
 rtThat(g
-etField(graphite, 
+raphiteFieldAccessor.getField(
 %22port%22))
@@ -2182,34 +2182,46 @@
 rtThat(g
-etField(graphite, 
+raphiteFieldAccessor.getField(
 %22address
@@ -2653,38 +2653,152 @@
     
-assertThat(getField(graphite, 
+final FieldAccessor%3CGraphiteUDP%3E graphiteUDPFieldAccessor = new FieldAccessor%3C%3E(graphite);%0A        assertThat(graphiteUDPFieldAccessor.getField(
 %22hos
@@ -2850,34 +2850,49 @@
 rtThat(g
-etField(graphite, 
+raphiteUDPFieldAccessor.getField(
 %22port%22))
@@ -2933,26 +2933,41 @@
 at(g
-etField(graphite, 
+raphiteUDPFieldAccessor.getField(
 %22add
@@ -3014,274 +3014,106 @@
 tic 
-Object getField(GraphiteUDP graphite, String name) throws NoSuchFieldException %7B%0A        try %7B%0A            return getInaccessibleField(GraphiteUDP.class, name).get(graphite);%0A        %7D catch (IllegalAccessException e) %7B%0A            throw new IllegalStateException(e)
+class FieldAccessor%3CT%3E %7B%0A        T obj;%0A%0A        FieldAccessor(T obj) %7B%0A            this.obj = obj
 ;%0A  
@@ -3124,33 +3124,16 @@
   %7D%0A
- 
+%0A
    
-%7D%0A%0A
     
-private static
  Obj
@@ -3149,27 +3149,8 @@
 eld(
-Graphite graphite, 
 Stri
@@ -3169,310 +3169,31 @@
 ows 
-NoSuchFieldException %7B%0A        try %7B%0A            return getInaccessibleField(Graphite.class, name).get(graphite);%0A        %7D catch (IllegalAccessException e) %7B%0A            throw new IllegalStateException(e);%0A        %7D%0A    %7D%0A%0A    private static Field getInaccessibleField(Class klass, String name) throws
+IllegalAccessException,
  NoS
@@ -3216,24 +3216,28 @@
 n %7B%0A        
+    
 Field field 
@@ -3242,13 +3242,22 @@
 d = 
-k
+obj.getC
 lass
+()
 .get
@@ -3285,16 +3285,20 @@
         
+   
+ 
 field.se
@@ -3320,24 +3320,28 @@
 e);%0A        
+    
 return field
@@ -3340,17 +3340,36 @@
 rn field
-;
+.get(obj);%0A        %7D
 %0A    %7D%0A%7D

Improve type safety of `GraphiteReporterFactoryTest`