package slacknotifications.teamcity.extension;

import jetbrains.buildServer.controllers.BaseController;
import jetbrains.buildServer.serverSide.SBuildServer;
import jetbrains.buildServer.serverSide.SProject;
import jetbrains.buildServer.serverSide.auth.Permission;
import jetbrains.buildServer.serverSide.settings.ProjectSettingsManager;
import jetbrains.buildServer.users.SUser;
import jetbrains.buildServer.web.openapi.PluginDescriptor;
import jetbrains.buildServer.web.openapi.WebControllerManager;
import jetbrains.buildServer.web.util.SessionUser;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.servlet.ModelAndView;
import slacknotifications.SlackNotification;
import slacknotifications.teamcity.BuildState;
import slacknotifications.teamcity.BuildStateEnum;
import slacknotifications.teamcity.TeamCityIdResolver;
import slacknotifications.teamcity.extension.bean.ProjectSlackNotificationsBean;
import slacknotifications.teamcity.extension.bean.ProjectSlackNotificationsBeanJsonSerialiser;
import slacknotifications.teamcity.payload.SlackNotificationPayloadManager;
import slacknotifications.teamcity.settings.SlackNotificationContentConfig;
import slacknotifications.teamcity.settings.SlackNotificationMainSettings;
import slacknotifications.teamcity.settings.SlackNotificationProjectSettings;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;


public class SlackNotificationAjaxEditPageController extends BaseController {

	    private static final String PROJECT_ID = "projectId";
	    private static final String SLACK_NOTIFICATION = "slackNotifications";
	    private static final String SUBMIT_ACTION = "submitAction";
	    private static final String ERRORS_TAG = "<errors />";
	    private static final String MESSAGES = "messages";
	    private static final String CHANNEL = "channel";
	    private static final String SLACK_NOTIFICATION_ID = "slackNotificationId";
	    private static final String MAX_COMMITS_DISPLAY = "maxCommitsToDisplay";
	    private static final String BOT_NAME = "botName";
	    private static final String ICON_URL = "iconUrl";
	    private static final String FALSE = "false";
		protected static final String BEFORE_FINISHED = "BeforeFinished";
		protected static final String BUILD_INTERRUPTED = "BuildInterrupted";
		protected static final String BUILD_STARTED = "BuildStarted";
		protected static final String BUILD_BROKEN = "BuildBroken";
		protected static final String BUILD_FIXED = "BuildFixed";
		protected static final String BUILD_FAILED = "BuildFailed";
		protected static final String BUILD_SUCCESSFUL = "BuildSuccessful";
		
		
		private final WebControllerManager myWebManager;
    private final SlackNotificationMainSettings myMainSettings;
    private SBuildServer myServer;
	    private ProjectSettingsManager mySettings;
	    private final String myPluginPath;
	    private final SlackNotificationPayloadManager myManager;

	public SlackNotificationAjaxEditPageController(SBuildServer server, WebControllerManager webManager,
                                                       ProjectSettingsManager settings, SlackNotificationProjectSettings whSettings, SlackNotificationPayloadManager manager,
                                                       PluginDescriptor pluginDescriptor, SlackNotificationMainSettings mainSettings) {
	        super(server);
	        myWebManager = webManager;
	        myServer = server;
	        mySettings = settings;
	        myPluginPath = pluginDescriptor.getPluginResourcesPath();
	        myManager = manager;
            myMainSettings = mainSettings;
	    }

	    public void register(){
	      myWebManager.registerController("/slacknotifications/ajaxEdit.html", this);
	    }
	    
	    protected static void checkAndAddBuildState(HttpServletRequest r, BuildState state, BuildStateEnum myBuildState, String varName){
	    	if ((r.getParameter(varName) != null)
	    		&& ("on".equalsIgnoreCase(r.getParameter(varName)))){
	    		state.enable(myBuildState);
	    	} else {
	    		state.disable(myBuildState);;
	    	}
	    }
	    
	    protected static void checkAndAddBuildStateIfEitherSet(HttpServletRequest r, BuildState state, BuildStateEnum myBuildState, String varName, String otherVarName){
	    	if ((r.getParameter(varName) != null)
	    			&& ("on".equalsIgnoreCase(r.getParameter(varName)))){
	    		state.enable(myBuildState);
	    	} else if ((r.getParameter(otherVarName) != null)
	    			&& ("on".equalsIgnoreCase(r.getParameter(otherVarName)))){
		    	state.enable(myBuildState);
	    	} else {
	    		state.disable(myBuildState);;
	    	}
	    }

	    @Nullable
		@Override
	    protected ModelAndView doHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	    	
	        HashMap<String,Object> params = new HashMap<String,Object>();
	        
	        SUser myUser = SessionUser.getUser(request);
	        SProject myProject;
	        SlackNotificationProjectSettings projSettings = null;

	    	if ("post".equalsIgnoreCase(request.getMethod())){
	    		if ((request.getParameter(PROJECT_ID) != null)
	    			&& request.getParameter(PROJECT_ID).startsWith("project")){
	    		    	projSettings = (SlackNotificationProjectSettings) mySettings.getSettings(request.getParameter(PROJECT_ID), SLACK_NOTIFICATION);
	    		    	myProject = this.myServer.getProjectManager().findProjectById(request.getParameter(PROJECT_ID));

			    		if ((projSettings != null) && (myProject != null)
			    				&& (myUser.isPermissionGrantedForProject(myProject.getProjectId(), Permission.EDIT_PROJECT))){
			    			if ((request.getParameter(SUBMIT_ACTION) != null ) 
			    				&& ("removeSlackNotification".equals(request.getParameter(SUBMIT_ACTION)))
			    				&& (request.getParameter("removedSlackNotificationId") != null)){
			    					projSettings.deleteSlackNotification(request.getParameter("removedSlackNotificationId"), myProject.getProjectId());
			    					if(projSettings.updateSuccessful()){
			    						myProject.persist();
			    						params.put(MESSAGES, ERRORS_TAG);
			    					} else {
			    						params.put(MESSAGES, "<errors><error id=\"messageArea\">The slacknotifications was not found. Have the SlackNotifications been edited on disk or by another user?</error></errors>");
			    					}
			    					
			    			} else if ((request.getParameter(SUBMIT_ACTION) != null ) 
				    				&& ("updateSlackNotification".equals(request.getParameter(SUBMIT_ACTION)))){
			    				if((request.getParameter(CHANNEL) != null )
				    				&& (request.getParameter(CHANNEL).length() > 0 )){
			    					
			    					if (request.getParameter(SLACK_NOTIFICATION_ID) != null){
			    						Boolean enabled = false;
                                        Boolean mentionChannelEnabled = false;
										Boolean mentionSlackUserEnabled = false;
			    						Boolean buildTypeAll = false;
			    						Boolean buildTypeSubProjects = false;
                                        SlackNotificationContentConfig content = new SlackNotificationContentConfig();
			    						Set<String> buildTypes = new HashSet<String>();
			    						if ((request.getParameter("slackNotificationsEnabled") != null )
			    								&& ("on".equalsIgnoreCase(request.getParameter("slackNotificationsEnabled")))){
			    							enabled = true;
			    						}
                                        if ((request.getParameter("mentionChannelEnabled") != null )
                                                && ("on".equalsIgnoreCase(request.getParameter("mentionChannelEnabled")))){

                                            mentionChannelEnabled = true;
                                        }
										if ((request.getParameter("mentionSlackUserEnabled") != null )
												&& ("on".equalsIgnoreCase(request.getParameter("mentionSlackUserEnabled")))){
											mentionSlackUserEnabled = true;
										}

                                        content.setEnabled((request.getParameter("customContentEnabled") != null )
                                                && ("on".equalsIgnoreCase(request.getParameter("customContentEnabled"))));

                                        if (content.isEnabled()){

                                            if ((request.getParameter(MAX_COMMITS_DISPLAY) != null )
                                                    && (request.getParameter(MAX_COMMITS_DISPLAY).length() > 0)){
                                                content.setMaxCommitsToDisplay(convertToInt(request.getParameter(MAX_COMMITS_DISPLAY), SlackNotificationContentConfig.DEFAULT_MAX_COMMITS));
                                            }

                                            content.setShowBuildAgent((request.getParameter("showBuildAgent") != null )
                                                    && ("on".equals(request.getParameter("showBuildAgent"))));

											content.setShowBuildStatus((request.getParameter("showBuildStatus") != null )
													&& ("on".equals(request.getParameter("showBuildStatus"))));

											content.setShowTitle((request.getParameter("showTitle") != null )
													&& ("on".equals(request.getParameter("showTitle"))));

                                            content.setShowCommits((request.getParameter("showCommits") != null )
                                                    && ("on".equalsIgnoreCase(request.getParameter("showCommits"))));

                                            content.setShowCommitters((request.getParameter("showCommitters") != null)
                                                    && ("on".equalsIgnoreCase(request.getParameter("showCommitters"))));

                                            content.setShowElapsedBuildTime((request.getParameter("showElapsedBuildTime") != null)
                                                    && ("on".equalsIgnoreCase(request.getParameter("showElapsedBuildTime"))));

                                            content.setShowFailureReason((request.getParameter("showFailureReason") != null)
                                                    && ("on".equalsIgnoreCase(request.getParameter("showFailureReason"))));

                                            if ((request.getParameter(BOT_NAME) != null )
                                                    && (request.getParameter(BOT_NAME).length() > 0)){
                                                content.setBotName(request.getParameter(BOT_NAME));
                                            }

                                            if ((request.getParameter(ICON_URL) != null )
                                                    && (request.getParameter(ICON_URL).length() > 0)){
                                                content.setIconUrl(request.getParameter(ICON_URL));
                                            }
                                        }

			    						BuildState states = new BuildState();
			    						
			    						checkAndAddBuildState(request, states, BuildStateEnum.BUILD_SUCCESSFUL, BUILD_SUCCESSFUL);
			    						checkAndAddBuildState(request, states, BuildStateEnum.BUILD_FAILED, BUILD_FAILED);
			    						checkAndAddBuildState(request, states, BuildStateEnum.BUILD_FIXED, BUILD_FIXED);
			    						checkAndAddBuildState(request, states, BuildStateEnum.BUILD_BROKEN, BUILD_BROKEN);
			    						checkAndAddBuildState(request, states, BuildStateEnum.BUILD_STARTED, BUILD_STARTED);
			    						checkAndAddBuildState(request, states, BuildStateEnum.BUILD_INTERRUPTED, BUILD_INTERRUPTED);	
			    						checkAndAddBuildState(request, states, BuildStateEnum.BEFORE_BUILD_FINISHED, BEFORE_FINISHED);
			    						checkAndAddBuildStateIfEitherSet(request, states, BuildStateEnum.BUILD_FINISHED, BUILD_SUCCESSFUL, BUILD_FAILED);
			    						checkAndAddBuildState(request, states, BuildStateEnum.RESPONSIBILITY_CHANGED, "ResponsibilityChanged");
			    						
			    						if ((request.getParameter("buildTypeSubProjects") != null ) && ("on".equalsIgnoreCase(request.getParameter("buildTypeSubProjects")))){
			    							buildTypeSubProjects = true;
			    						}
			    						if ((request.getParameter("buildTypeAll") != null ) && ("on".equalsIgnoreCase(request.getParameter("buildTypeAll")))){
			    							buildTypeAll = true;
			    						} else {
			    							if (request.getParameterValues("buildTypeId") != null){
			    								String[] types = request.getParameterValues("buildTypeId");
			    								for (String string : types) {
			    									buildTypes.add(string);
												}
			    							}
			    						}
		    						
			    						if ("new".equals(request.getParameter(SLACK_NOTIFICATION_ID))){
			    							projSettings.addNewSlackNotification(myProject.getProjectId(), request.getParameter("token"), request.getParameter(CHANNEL), request.getParameter("team"), enabled,
													states, buildTypeAll, buildTypeSubProjects, buildTypes, mentionChannelEnabled, mentionSlackUserEnabled);
			    							if(projSettings.updateSuccessful()){
			    								myProject.persist();
			    	    						params.put(MESSAGES, ERRORS_TAG);
			    							} else {
			    								params.put("message", "<errors><error id=\"\">" + projSettings.getUpdateMessage() + "</error>");
			    							}
			    						} else {
			    							projSettings.updateSlackNotification(myProject.getProjectId(), request.getParameter("token"),
                                                    request.getParameter(SLACK_NOTIFICATION_ID), request.getParameter(CHANNEL), enabled,
													states, buildTypeAll, buildTypeSubProjects, buildTypes, mentionChannelEnabled,
													mentionSlackUserEnabled, content);
			    							if(projSettings.updateSuccessful()){
			    								myProject.persist();
			    	    						params.put(MESSAGES, ERRORS_TAG);
			    							} else {
			    								params.put("message", "<errors><error id=\"\">" + projSettings.getUpdateMessage() + "</error>");
			    							}
			    						}
			    					} // TODO Need to handle slackNotificationId being null
			    						
			    				} else {
			    					if ((request.getParameter(CHANNEL) == null )
				    				|| (request.getParameter(CHANNEL).length() == 0)){
			    						params.put(MESSAGES, "<errors><error id=\"emptySlackNotificationChannel\">Please enter a channel.</error></errors>");
			    					}
			    				}
				    			
			    			}
			    		} else {
			    			params.put(MESSAGES, "<errors><error id=\"messageArea\">You do not appear to have permission to edit SlackNotifications.</error></errors>");
			    		}
	    		}
	    	}

	    	if ("get".equalsIgnoreCase(request.getMethod())
	        		&& request.getParameter(PROJECT_ID) != null 
	        		&& request.getParameter(PROJECT_ID).startsWith("project")){
	        	
		    	SlackNotificationProjectSettings projSettings1 = (SlackNotificationProjectSettings) mySettings.getSettings(request.getParameter(PROJECT_ID), SLACK_NOTIFICATION);
		    	SProject project = this.myServer.getProjectManager().findProjectById(request.getParameter(PROJECT_ID));
		    	
		    	String message = projSettings1.getSlackNotificationsAsString();
		    	
		    	params.put("haveProject", "true");
		    	params.put(MESSAGES, message);
		    	params.put(PROJECT_ID, project.getProjectId());
		    	params.put("projectExternalId", TeamCityIdResolver.getExternalProjectId(project));
		    	params.put("projectName", project.getName());
		    	
		    	params.put("slackNotificationCount", projSettings1.getSlackNotificationsCount());
		    	if (projSettings1.getSlackNotificationsCount() == 0){
		    		params.put("noSlackNotifications", "true");
		    		params.put(SLACK_NOTIFICATION, FALSE);
		    	} else {
		    		params.put("noSlackNotifications", FALSE);
		    		params.put(SLACK_NOTIFICATION, "true");
		    		params.put("slackNotificationList", projSettings.getSlackNotificationsAsList());
		    		params.put("slackNotificationsDisabled", !projSettings.isEnabled());
		    		params.put("slackNotificationsEnabledAsChecked", projSettings.isEnabledAsChecked());
		    		params.put("projectSlackNotificationsAsJson", ProjectSlackNotificationsBeanJsonSerialiser.serialise(ProjectSlackNotificationsBean.build(projSettings, project, myMainSettings)));
		    	}
	        } else {
	        	params.put("haveProject", FALSE);
	        }
	        
	        return new ModelAndView(myPluginPath + "SlackNotification/ajaxEdit.jsp", params);
	    }


    private int convertToInt(String s, int defaultValue){
        try{
            int myInt = Integer.parseInt(s);
            return myInt;
        } catch (NumberFormatException e){
            return defaultValue;
        }
    }
}
