package com.baloise.jenkins.plugin;

import static java.lang.String.format;

import java.io.IOException;
import java.util.Set;

import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

import com.github.baloise.rocketchatrestclient.RocketChatClient;
import com.github.baloise.rocketchatrestclient.model.Room;

import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.model.AbstractProject;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.View;
import hudson.model.listeners.RunListener;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;

@Extension
public class RocketChatNotifier extends RunListener<Run<?, ?>> implements Describable<RocketChatNotifier>, ExtensionPoint, ViewListener {

	
	ViewTracker viewTracker = new ViewTracker().addViewListener(this);
	
	@Override
	public void fireViewChanged(View view, Result oldResult, Result newResult) {
		chat(format("view/%s, %s -> %s", view.getDisplayName(), oldResult, newResult));
	}
	
	@Override
	public void onStarted(Run<?, ?> r, TaskListener listener) {
		notify(r, listener);
	}
	
	@Override
	public void onCompleted(Run<?, ?> r, TaskListener listener) {
		notify(r, listener);
	}
	
	public void notify(Run<?, ?> run, TaskListener listener) {
		if(getDescriptor().getNotifyBuilds()) {
			String resultMessage = run.isBuilding() ? "STARTED" : run.getResult().toString();
			String message = format("%s, %s, %s, %s", run.getParent().getDisplayName(), run.getDisplayName(), resultMessage, run.getBuildStatusSummary().message);
			chat(message, listener);
		}
		if(getDescriptor().getNotifyViews()) {
			viewTracker.trackViews(run);
		} else {
			viewTracker.disable();
		}
	}

	private void chat(String message) {
		chat(message, null);
	}
	
	private void chat(final String message, final TaskListener listener) {
		if(listener != null) listener.getLogger().println(format("Notifying %s/%s '%s'", getDescriptor().getUrl(), getDescriptor().getRoom(), message));
		try {
			getDescriptor().getRocketChatClient().send(getDescriptor().getRoom(), message);
		} catch (IOException e) {
			e.printStackTrace();
			if(listener != null) listener.getLogger().println("Rocket.Chat Notification failed");
		}
	}

	@Extension 
	public static final class DescriptorImpl extends Descriptor<RocketChatNotifier> {

		 public DescriptorImpl() {
	            load();
	     }
		
		private String url;
		private String user;
		private String password;
		private String room;
		private Boolean notifyBuildsDisabled;
		private Boolean notifyViewsDisabled;

		private transient RocketChatClient lazyRcClient;

		public FormValidation doCheckUrl(@QueryParameter String value) {
			return empty(value);
		}
		public FormValidation doCheckUser(@QueryParameter String value) {
			return  empty(value);
		}
		public FormValidation doCheckPassword(@QueryParameter String value) {
			return empty(value);
		}
		public FormValidation doCheckRoom(@QueryParameter String value) {
			return empty(value);
		}

		public FormValidation doTestConnection(
				 @QueryParameter("url") final String url,
				 @QueryParameter("user") final String user,
				 @QueryParameter("password") final String password,
				 @QueryParameter("room") final String room
				)  {
			try {
				RocketChatClient rcClient = new RocketChatClient(url, user, password);
				Set<Room> publicRooms = rcClient.getPublicRooms();
				StringBuilder message = new StringBuilder("available rooms are: ");
				boolean comma = false;
				for (Room r : publicRooms) {
					if(r.name.equals(room))
						return FormValidation.ok("Server version is "+rcClient.getRocketChatVersion());
					if(comma) message.append(", ");							
					comma = true;
					message.append("'"+r.name+"'");
				}
				return FormValidation.error("available rooms are "+message);
			} catch (Exception e) {
				return FormValidation.error(e.getMessage());
			}
		}
		
		public RocketChatClient getRocketChatClient() {
			if(lazyRcClient == null) {
				lazyRcClient = new RocketChatClient(url, user, password);
			}
			return lazyRcClient;
		}

		private FormValidation empty(String value) {
			return (value == null || value.isEmpty()) ? FormValidation.error("Must not be empty") : FormValidation.ok();
		}

		public boolean isApplicable(Class<? extends AbstractProject> aClass) {
			return true;
		}

		public String getDisplayName() {
			return "Rocket.Chat Notifier";
		}

		@Override
		public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
			req.bindJSON(this, formData);
			save();
			return super.configure(req, formData);
		}

		@Override
		public synchronized void load() {
			lazyRcClient = null;
			super.load();
		}
		
		@Override
		public synchronized void save() {
			lazyRcClient = null;
			super.save();
		}
		
		public String getUrl() {
			return url;
		}

		public void setUrl(String url) {
			this.url = url;
		}

		public String getUser() {
			return user;
		}

		public void setUser(String user) {
			this.user = user;
		}

		public String getPassword() {
			return password;
		}

		public void setPassword(String password) {
			this.password = password;
		}

		public String getRoom() {
			return room;
		}

		public void setRoom(String room) {
			this.room = room;
		}
		
		public boolean getNotifyBuilds() {
			return getNotifyBuildsDisabled() == null || !getNotifyBuildsDisabled();
		}
		
		public boolean getNotifyViews() {
			return getNotifyViewsDisabled() == null || !getNotifyViewsDisabled();
		}
	
		public Boolean getNotifyBuildsDisabled() {
			return notifyBuildsDisabled;
		}
		public void setNotifyBuildsDisabled(Boolean notifyBuildsDisabled) {
			this.notifyBuildsDisabled = notifyBuildsDisabled;
		}
		public Boolean getNotifyViewsDisabled() {
			return notifyViewsDisabled;
		}
		public void setNotifyViewsDisabled(Boolean notifyViewsDisabled) {
			this.notifyViewsDisabled = notifyViewsDisabled;
		}

	}

	public DescriptorImpl getDescriptor() {
		return (DescriptorImpl) Jenkins.getInstance().getDescriptorOrDie(getClass());
	}
}