package jetbrains.buildServer.profile;

import jetbrains.buildServer.controllers.*;
import jetbrains.buildServer.serverSide.auth.*;
import jetbrains.buildServer.log.Loggers;
import jetbrains.buildServer.serverSide.SBuildServer;
import jetbrains.buildServer.util.StringUtil;
import jetbrains.buildServer.web.openapi.WebControllerManager;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;

public class ProfilerController extends BaseController {
  private final Profiler myProfiler;

  public ProfilerController(@NotNull final SBuildServer server,
                            @NotNull final WebControllerManager manager,
                            @NotNull final Profiler profiler,
                            @NotNull final AuthorizationInterceptor authInterceptor) {
    super(server);
    myProfiler = profiler;
    String url = "/admin/profiler.html";
    manager.registerController(url, this);
    final RequestPermissionsChecker permissionsChecker = new RequestPermissionsChecker() {
      public void checkPermissions(@NotNull final AuthorityHolder authorityHolder, @NotNull final HttpServletRequest request)
        throws AccessDeniedException {
        if (!authorityHolder.isPermissionGrantedGlobally(Permission.CHANGE_SERVER_SETTINGS)) {
          throw new AccessDeniedException(authorityHolder, "You do not have enough permissions to start / stop server profiling");
        }
      }
    };
    authInterceptor.addPathBasedPermissionsChecker(url, permissionsChecker);
  }

  @Nullable
  protected ModelAndView doHandle(@NotNull final HttpServletRequest request, @NotNull final HttpServletResponse response) throws Exception {
    new AjaxRequestProcessor().processRequest(request, response, new AjaxRequestProcessor.RequestHandler() {
      public void handleRequest(@NotNull final HttpServletRequest request, @NotNull final HttpServletResponse response, @NotNull final Element xmlResponse) {
        final String action = request.getParameter("profilerAction");
        if (action == null) return;
        try {
          doAction(request, action);
        } catch (Exception e) {
          Loggers.SERVER.warnAndDebugDetails("Failed to handle profiler action '" + action + "'", e);
          ActionErrors errors = new ActionErrors();
          errors.addError("profilerProblem", getMessageWithNested(e));
          errors.serialize(xmlResponse);
        }
      }
    });

    return null;
  }

  static private String getMessageWithNested(Throwable e) {
    String result = e.getMessage();
    Throwable cause = e.getCause();
    if (cause != null) {
      result += " Caused by: " + getMessageWithNested(cause);
    }
    return result;
  }

  private void doAction(final HttpServletRequest request, final String action) throws Exception {
    if ("StartCpu".equals(action)) {
      myProfiler.startProfiler();
    } else if ("CaptureCPU".equals(action)) {
      try {
        String result = myProfiler.captureAndStop();
        addSuccessMessage(request, "CPU snapshot is saved to the file on the server: \"" + result + "\"" + getFileSizeString(result));
      } catch (Exception e) {
        throw new Exception("Cannot capture CPU snapshot.", e);
      }
    } else if ("StopCPU".equals(action)) {
      myProfiler.stopProfiler();
    } else if ("CaptureMemory".equals(action)) {
      try {
        String result = myProfiler.captureMemorySnapshot();
        addSuccessMessage(request, "Memory snapshot is saved to the file on the server: \"" + result + "\"" + getFileSizeString(result));
      } catch (Exception e) {
        throw new Exception("Cannot capture memory snapshot.", e);
      }
    }
  }

  private String getFileSizeString(final String fileName) {
    try {
      return " (" + StringUtil.formatFileSize(new File(fileName).length()) + ")";
    } catch (Exception e) {
      return "";
    }
  }

  private void addSuccessMessage(HttpServletRequest request, String result) {
    if (result != null) {
      getOrCreateMessages(request).addMessage("profilerMessage", result);
    }
  }
}
