package io.quarkus.opentelemetry.deployment;

import static java.util.Comparator.comparingLong;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import javax.enterprise.context.ApplicationScoped;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.quarkus.arc.Unremovable;

@Unremovable
@ApplicationScoped
public class TestSpanExporter implements SpanExporter {
    private final List<SpanData> finishedSpanItems = new ArrayList<>();
    private boolean isStopped = false;

    /**
     * Careful when retrieving the list of finished spans. There is a chance when the response is already sent to the
     * client and Vert.x still writing the end of the spans. This means that a response is available to assert from the
     * test side but not all spans may be available yet. For this reason, this method requires the number of expected
     * spans.
     */
    public List<SpanData> getFinishedSpanItems(int spanCount) {
        synchronized (this) {
            assertSpanCount(spanCount);
            return finishedSpanItems.stream().sorted(comparingLong(SpanData::getStartEpochNanos).reversed())
                    .collect(Collectors.toList());
        }
    }

    public void assertSpanCount(int spanCount) {
        synchronized (this) {
            await().atMost(30, SECONDS).untilAsserted(() -> assertEquals(spanCount, finishedSpanItems.size()));
        }
    }

    public void reset() {
        synchronized (this) {
            finishedSpanItems.clear();
        }
    }

    @Override
    public CompletableResultCode export(Collection<SpanData> spans) {
        synchronized (this) {
            if (isStopped) {
                return CompletableResultCode.ofFailure();
            }
            finishedSpanItems.addAll(spans);
        }
        return CompletableResultCode.ofSuccess();
    }

    @Override
    public CompletableResultCode flush() {
        return CompletableResultCode.ofSuccess();
    }

    @Override
    public CompletableResultCode shutdown() {
        synchronized (this) {
            finishedSpanItems.clear();
            isStopped = true;
        }
        return CompletableResultCode.ofSuccess();
    }
}

@@ -255,36 +255,8 @@
 s;%0A%0A
-import java.util.ArrayList;%0A
 impo
@@ -303,16 +303,66 @@
 l.List;%0A
+import java.util.concurrent.CopyOnWriteArrayList;%0A
 import j
@@ -785,16 +785,27 @@
 s = new 
+CopyOnWrite
 ArrayLis
@@ -823,16 +823,25 @@
 private 
+volatile 
 boolean 
@@ -1314,42 +1314,8 @@
 ) %7B%0A
-        synchronized (this) %7B%0A    
     
@@ -1346,20 +1346,16 @@
 Count);%0A
-    
         
@@ -1467,20 +1467,16 @@
         
-    
 .collect
@@ -1494,34 +1494,24 @@
 .toList());%0A
-        %7D%0A
     %7D%0A%0A    p
@@ -1558,42 +1558,8 @@
 ) %7B%0A
-        synchronized (this) %7B%0A    
     
@@ -1662,26 +1662,16 @@
 ze()));%0A
-        %7D%0A
     %7D%0A%0A 
@@ -1699,42 +1699,8 @@
 ) %7B%0A
-        synchronized (this) %7B%0A    
     
@@ -1730,26 +1730,16 @@
 lear();%0A
-        %7D%0A
     %7D%0A%0A 
@@ -1825,42 +1825,8 @@
 ) %7B%0A
-        synchronized (this) %7B%0A    
     
@@ -1842,28 +1842,24 @@
 sStopped) %7B%0A
-    
             
@@ -1912,24 +1912,18 @@
     
-    %7D%0A  
+%7D%0A
         
   fi
@@ -1910,34 +1910,32 @@
       %7D%0A        
-  
 finishedSpanItem
@@ -1951,26 +1951,16 @@
 spans);%0A
-        %7D%0A
         
@@ -2186,42 +2186,8 @@
 ) %7B%0A
-        synchronized (this) %7B%0A    
     
@@ -2225,20 +2225,16 @@
         
-  
-  
 isStoppe
@@ -2243,26 +2243,16 @@
 = true;%0A
-        %7D%0A
         

Use a concurrent collection