package study.runtime;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import org.apache.coyote.AbstractProtocol;
import org.springframework.boot.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.context.WebServerApplicationContext;
import org.springframework.boot.web.server.context.WebServerInitializedEvent;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import tools.jackson.databind.json.JsonMapper;

@RestController
@RequestMapping("/lab")
class ServerProbe {
  static final List<String> dispatches=new CopyOnWriteArrayList<>();
  private final ConfigurableApplicationContext context;
  private final AtomicInteger active=new AtomicInteger(),completed=new AtomicInteger(),uploads=new AtomicInteger();
  ServerProbe(ConfigurableApplicationContext context){this.context=context;}
  @EventListener void bound(WebServerInitializedEvent event){System.out.println("BOUND "+event.getWebServer().getPort());}
  @GetMapping("/state") Map<String,Object> state(){
    var web=(TomcatWebServer)((WebServerApplicationContext)context).getWebServer();
    var protocol=(AbstractProtocol<?>)web.getTomcat().getConnector().getProtocolHandler();
    var settings=new LinkedHashMap<String,Object>();
    settings.put("maxThreads",protocol.getMaxThreads());settings.put("minSpareThreads",protocol.getMinSpareThreads());settings.put("maxConnections",protocol.getMaxConnections());settings.put("acceptCount",protocol.getAcceptCount());settings.put("connectionTimeoutMillis",protocol.getConnectionTimeout());
    return Map.of("active",active.get(),"completed",completed.get(),"uploads",uploads.get(),"connector",settings,"mapper",context.getBean(JsonMapper.class).getClass().getName(),"mapperVersion",context.getBean(JsonMapper.class).version().toString());
  }
  public record Input(String value){}
  public record Reply(String value,String optional){}
  @PostMapping(path="/json",consumes=MediaType.APPLICATION_JSON_VALUE) Reply json(@RequestBody Input input){return new Reply(input.value(),null);}
  @PostMapping(path="/upload",consumes=MediaType.MULTIPART_FORM_DATA_VALUE) Map<String,Object> upload(@RequestParam("file") MultipartFile file)throws IOException{
    byte[] bytes=file.getBytes();uploads.incrementAndGet();return Map.of("bytes",bytes.length,"accepted",true);
  }
  @GetMapping("/error") void error(HttpServletResponse response)throws IOException{response.sendError(500,"synthetic-error");}
  @GetMapping("/trace") List<String> trace(){return List.copyOf(dispatches);}
  @GetMapping("/slow") Map<String,Object> slow(@RequestParam(defaultValue="1200") long millis)throws InterruptedException{
    if(millis<0||millis>10000)throw new IllegalArgumentException("millis must be within 0..10000");
    active.incrementAndGet();System.out.println("SLOW ENTER millis="+millis);
    try{Thread.sleep(millis);completed.incrementAndGet();System.out.println("SLOW COMPLETED");return Map.of("completed",true);}
    finally{active.decrementAndGet();System.out.println("SLOW EXIT");}
  }
  @Configuration(proxyBeanMethods=false)
  static class DispatchProbe {
    @Bean FilterRegistrationBean<Filter> dispatchFilter(){
      Filter filter=(request,response,chain)->{
        var http=(HttpServletRequest)request;
        dispatches.add(http.getDispatcherType()+" "+http.getRequestURI()+" original="+http.getAttribute(RequestDispatcher.ERROR_REQUEST_URI));
        chain.doFilter(request,response);
      };
      var registration=new FilterRegistrationBean<>(filter);registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ERROR);registration.addUrlPatterns("/*");return registration;
    }
  }
}
