package jetbrains.buildServer.serverSide;

import jetbrains.buildServer.controllers.BaseController;
import jetbrains.buildServer.serverSide.auth.SecurityContext;
import jetbrains.buildServer.users.User;
import jetbrains.buildServer.web.openapi.PluginDescriptor;
import jetbrains.buildServer.web.openapi.WebControllerManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONException;
import org.json.JSONWriter;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author Eugene Petrenko
 *         Created: 31.03.2010 12:38:12
 */
public class PluginsChatController extends BaseController {
  private final SecurityContext mySecutiry;
  private final ChatModel myModel;
  private final ConcurrentMap<String, Integer> myFromOffsetMap = new ConcurrentHashMap<String,Integer>();

  public PluginsChatController(@NotNull WebControllerManager wcm,
                               SecurityContext secutiry,
                               PluginDescriptor descr,
                               ChatModel model) {
    mySecutiry = secutiry;
    myModel = model;
    wcm.registerController(descr.getPluginResourcesPath("chatty-batty.html"), this);
  }

  @Override
  protected ModelAndView doHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    prepartResponse(response);
    final String action = request.getParameter("act");
    final boolean hasAll = "true".equalsIgnoreCase(request.getParameter("adminMode"));

    if ("list".equals(action)) {
      int offset = getOffset(request);
      if (!hasAll) {
        return sendCurrentMessages(request, response, offset);
      } else {
        final ChatMessagesCollection chatMessages = myModel.getAllMessages(offset);
        rememberLastId(request, chatMessages);
        return writeJSONResponce(response, chatMessages);
      }
    }

    if ("start".equals(action)) {
      final ChatMessagesCollection obj = myModel.initializeChat(getUser(request));
      rememberLastId(request, obj);
      return writeJSONResponce(response, obj);
    }

    if ("close".equals(action)) {
      myFromOffsetMap.remove(getClientHash(request));
      return writeJSONResponce(response, TEAMCITY_RULEZ);
    }

    if ("say".equals(action)) {
      return postMessage(request, response);
    }

    return writeJSONResponce(response, UNKNOWN_OBJECT_ERROR);
  }

  private ModelAndView postMessage(HttpServletRequest request, HttpServletResponse response) throws IOException, JSONException {
    String message = jetbrains.buildServer.util.StringUtil.truncateStringValueWithDotsAtEnd(request.getParameter("message"), 300);

    User user = getUser(request);
    final ChatMessagesCollection post = myModel.postMessage(user, message, getOffset(request));
    rememberLastId(request, post);
    return writeJSONResponce(response, post);
  }

  private void rememberLastId(HttpServletRequest request, ChatMessagesCollection post) {
    myFromOffsetMap.put(getClientHash(request), post.getLastMessageId());
  }

  private ModelAndView sendCurrentMessages(HttpServletRequest request, HttpServletResponse response, int offset) throws IOException, JSONException {
    final User user = getUser(request);
    final ChatMessagesCollection chatMessages = myModel.getMessagesToUser(user, offset);
    rememberLastId(request, chatMessages);
    return writeJSONResponce(response, chatMessages);
  }

  private ModelAndView writeJSONResponce(HttpServletResponse responce, JSONAble obj) throws IOException, JSONException {
    final PrintWriter writer = responce.getWriter();
    JSONWriter w = new JSONWriter(writer);
    obj.writeJSON(w);
    writer.flush();
    writer.close();
    return null;
  }

  private void prepartResponse(HttpServletResponse responce) {
    responce.setContentType("text/x-json");
    responce.setCharacterEncoding("utf-8");
    responce.setDateHeader("Expires", 1270042079742L);
    responce.setDateHeader("Last-Modified", 1270042089742L);
  }

  ///////////////////////////////////////////////////////////////////////////////

  @Nullable
  private User getUser(HttpServletRequest request) {
    return request.getParameter("all-users") != null ? null : mySecutiry.getAuthorityHolder().getAssociatedUser();
  }

  @NotNull
  private String getClientHash(HttpServletRequest request) {
    final User user = getUser(request);
    return "2" + (user == null ? "<null>" : user.getUsername()) + "#" + String.valueOf(request.getParameter("id")) + "#" + String.valueOf(request.getParameter("all"));
  }


  private int getOffset(HttpServletRequest request) {
    final Integer id = myFromOffsetMap.get(getClientHash(request));
    if (id != null) {
      return id;
    }

    int offset;
    try {
      offset = Integer.parseInt(request.getParameter("from"));
    } catch (Exception e) {
      offset = 0;
    }
    return offset;
  }


  private final JSONAble TEAMCITY_RULEZ = new JSONAble() {
    public void writeJSON(JSONWriter w) throws JSONException {
      w.object().key("teamcity").value("rulezzz!").endObject();
    }
  };

  private final JSONAble UNKNOWN_OBJECT_ERROR = new JSONAble() {
    public void writeJSON(JSONWriter w) throws JSONException {
      w.object().key("error").value("Unknown action").endObject();
    }
  };
}
