package study.ready;

import java.util.*;
import java.util.concurrent.TimeUnit;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.tracing.Tracer;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import org.springframework.boot.actuate.audit.*;
import org.springframework.boot.actuate.audit.listener.AuditApplicationEvent;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.boot.availability.*;
import org.springframework.boot.info.BuildProperties;
import org.springframework.context.*;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestClient;
import tools.jackson.databind.json.JsonMapper;

@RestController
@RequestMapping("/lab")
class LabController {
  private final ConfigurableApplicationContext context;private final ApplicationAvailability availability;private final MeterRegistry meters;private final Tracer tracer;private final RestClient autoClient;private final String downstream;
  LabController(ConfigurableApplicationContext context,ApplicationAvailability availability,MeterRegistry meters,Tracer tracer,RestClient.Builder builder,Environment env){this.context=context;this.availability=availability;this.meters=meters;this.tracer=tracer;this.downstream=env.getRequiredProperty("lab.downstream-url");this.autoClient=builder.build();}
  @GetMapping("/open") Map<String,Object> open(){return Map.of("served",true,"readiness",availability.getReadinessState().name());}
  @GetMapping("/gated") ResponseEntity<?> gated(){return availability.getReadinessState()==ReadinessState.ACCEPTING_TRAFFIC?ResponseEntity.ok(Map.of("served",true)):ResponseEntity.status(503).body(Map.of("served",false));}
  @GetMapping("/state") Map<String,Object> state(){
    var result=new LinkedHashMap<String,Object>();
    result.put("events",List.copyOf(ReadinessApp.events));result.put("readiness",availability.getReadinessState().name());result.put("liveness",availability.getLivenessState().name());
    result.put("infoBean",context.getBeansOfType(InfoEndpoint.class).size());result.put("auditEndpointBean",context.getBeansOfType(AuditEventsEndpoint.class).size());result.put("auditRepository",context.getBeansOfType(AuditEventRepository.class).size());result.put("buildProperties",context.getBeansOfType(BuildProperties.class).size());result.put("defaultManagementChain",context.containsBean("managementSecurityFilterChain"));result.put("defaultUsers",context.containsBean("inMemoryUserDetailsManager"));result.put("authenticationAuditListener",context.containsBean("authenticationAuditListener"));
    var controlOperations=new ArrayList<String>();
    for(var endpoint:context.getBean(org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier.class).getEndpoints())
      if(endpoint.getEndpointId().toString().equals("labcontrol"))for(var operation:endpoint.getOperations())controlOperations.add(operation.getType().name());
    result.put("controlOperations",controlOperations);return result;
  }
  @GetMapping("/count") Map<String,Object> count(){meters.counter("u24.orders.accepted","outcome","accepted").increment();return Map.of("accepted",true);}
  @GetMapping("/call") Map<String,Object> call(@RequestParam(defaultValue="false") boolean bare){
    String traceId=tracer.currentSpan()==null?"none":tracer.currentSpan().context().traceId();
    String body=(bare?RestClient.create():autoClient).get().uri(downstream).retrieve().body(String.class);
    return Map.of("traceId",traceId,"downstream",new JsonMapper().readValue(body,Map.class),"bare",bare);
  }
  @GetMapping("/spans") List<Map<String,Object>> spans(){context.getBean(SdkTracerProvider.class).forceFlush().join(2,TimeUnit.SECONDS);return context.getBean(RecordingExporter.class).snapshot();}
  @GetMapping("/audit") Map<String,Object> audit(){context.publishEvent(new AuditApplicationEvent("synthetic-operator","U24_ORDER_REPLAY",Map.of("order","synthetic-1")));return Map.of("published",true);}
}
