/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package brave.spring.webmvc;

import brave.Tracer;
import brave.http.HttpTracing;
import brave.test.http.ITServletContainer;
import brave.test.http.ITServlet25Container;
import brave.test.http.ServletContainer;
import java.io.IOException;
import java.util.EnumSet;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AssumptionViolatedException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;

import static org.springframework.web.servlet.DispatcherServlet.HANDLER_ADAPTER_BEAN_NAME;
import static org.springframework.web.servlet.DispatcherServlet.HANDLER_MAPPING_BEAN_NAME;

class ITSpanCustomizingHandlerInterceptor extends ITServletContainer {
  public ITSpanCustomizingHandlerInterceptor() {
    super(new Jetty7ServerController());
  }

  /** Overridden to support Jetty 7.x (that uses Servlet 2.5) */
  static final class Jetty7ServerController implements ServletContainer.ServerController {
    @Override public Server newServer(int port) {
      Server result = new Server();
      SocketConnector connector = new SocketConnector();
      connector.setMaxIdleTime(1000 * 60 * 60);
      connector.setPort(port);
      result.setConnectors(new Connector[] {connector});
      return result;
    }

    @Override public int getLocalPort(Server server) {
      return server.getConnectors()[0].getLocalPort();
    }
  }

  @Override public void notFound() {
    throw new AssumptionViolatedException("TODO: add MVC handling for not found");
  }

  @Override public void httpRoute() {
    throw new AssumptionViolatedException(
      "HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE doesn't exist until Spring 3");
  }

  @Override public void httpRoute_nested() {
    throw new AssumptionViolatedException(
      "HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE doesn't exist until Spring 3");
  }

  @Controller
  public static class TestController {
    final Tracer tracer;

    @Autowired public TestController(HttpTracing httpTracing) {
      this.tracer = httpTracing.tracing().tracer();
    }

    @RequestMapping(value = "/foo")
    public void foo(HttpServletResponse response) throws IOException {
      response.getWriter().write("foo");
    }

    @RequestMapping(value = "/badrequest")
    public void badrequest(HttpServletResponse response) throws IOException {
      response.sendError(400);
      response.flushBuffer();
    }

    @RequestMapping(value = "/child")
    public void child(HttpServletResponse response) throws IOException {
      tracer.nextSpan().name("child").start().finish();
      response.getWriter().write("child");
    }

    @RequestMapping(value = "/exception")
    public void notReady() throws UnavailableException {
      throw ITServlet25Container.NOT_READY_UE;
    }
  }

  @Override public void init(ServletContextHandler handler) {
    StaticWebApplicationContext wac = new StaticWebApplicationContext();
    wac.getBeanFactory()
      .registerSingleton("httpTracing", httpTracing);

    wac.getBeanFactory()
      .registerSingleton("testController", new TestController(httpTracing)); // the test resource

    DefaultAnnotationHandlerMapping mapping = new DefaultAnnotationHandlerMapping();
    mapping.setInterceptors(new Object[] {new SpanCustomizingHandlerInterceptor()});
    mapping.setApplicationContext(wac);

    wac.getBeanFactory().registerSingleton(HANDLER_MAPPING_BEAN_NAME, mapping);
    wac.getBeanFactory()
      .registerSingleton(HANDLER_ADAPTER_BEAN_NAME, new AnnotationMethodHandlerAdapter());

    handler.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
    handler.addServlet(new ServletHolder(new DispatcherServlet() {
      {
        wac.refresh();
        setDetectAllHandlerMappings(false);
        setDetectAllHandlerAdapters(false);
        setPublishEvents(false);
      }

      @Override protected WebApplicationContext initWebApplicationContext() throws BeansException {
        onRefresh(wac);
        return wac;
      }
    }), "/*");
    handler.addFilter(DelegatingTracingFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
  }
}
