package com.pmease.quickbuild.plugin.tracker.redmine;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.ws.rs.core.MediaType;

import org.apache.commons.httpclient.HttpStatus;
import org.dom4j.Document;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.pmease.quickbuild.plugin.report.engine.util.XMLHelper;
import com.pmease.quickbuild.plugin.tracker.core.TrackerException;
import com.pmease.quickbuild.plugin.tracker.core.extensionpoint.Issue;
import com.pmease.quickbuild.plugin.tracker.core.utils.RestClientFactory;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

public class RedmineCli {
	private static final Logger logger = LoggerFactory.getLogger(RedmineCli.class);
	
	private final String host;
	private final String username;
	private final String password;
	private final String projectId;
	
	public RedmineCli(String username, String password, String host, String projectId) {
		this.host = host;
		this.projectId = projectId;
		this.username = username;
		this.password = password;
	}

	public static RedmineCli newInstance() {
		RedmineSetting setting = RedmineSetting.get();
		return new RedmineCli(setting.getUser(), setting.getPassword(), setting.getHost(), setting.getAssociatedProject());
	}
	
	String getIssueDocument(String id) {
		logger.debug("Retriving issue " + id);
		
		ClientResponse response = getRestXMLResponse("/issues/" + id + ".xml");
		
		int status = response.getStatus();
		
		if (status == HttpStatus.SC_OK) {
			return response.getEntity(String.class);
		} else if (status == HttpStatus.SC_NOT_FOUND) {
			return null;
		} else {
			throw new TrackerException("Fetch issue " + id + " failed. " + response);
		}
	}
	
	public Issue getIssue(String id) {
		String doc = getIssueDocument(id);
		return RedmineHelper.parseIssue(doc);
	}
	
	public Issue createIssue(Map<String, String> options) {
		Document doc = RedmineHelper.mapToIssueDocument(options);
		System.out.println(doc.asXML());
		ClientResponse response = resource()
					.path("/projects/" + projectId + "/issues.xml")
					.type(MediaType.TEXT_XML)
					.post(ClientResponse.class, doc.asXML());
		
		int status = response.getStatus();
		String entity = response.getEntity(String.class);
		if (status == HttpStatus.SC_CREATED) {
			return RedmineHelper.parseIssue(entity);
		} else {
			throw new TrackerException("Create issue failed. " + response);
		}
	}
	
	public List<String> getProjects() {
		return Collections.emptyList();
	}
	
	private ClientResponse getRestXMLResponse(String path) {
		WebResource resource = resource().path(path);
		try {
			return resource.accept(MediaType.TEXT_XML).get(ClientResponse.class);
		} catch (Exception e) {
			throw new TrackerException("Unable to get response from URI " + path, e);
		}
	}
	
	private Document getRestXML(String path) {
		ClientResponse response = getRestXMLResponse(path);
		
		int status = response.getStatus();
		
		if (status != HttpStatus.SC_OK) {
			throw new TrackerException("Unable to get priorities from remote server with status " + status);
		}
		
		String xml = response.getEntity(String.class);
		return XMLHelper.readXML(xml);
	}
	
	private BiMap<Integer, String> getNamedEntities(String path, String elementName) {
		Document doc = getRestXML(path);
		BiMap<Integer, String> map = HashBiMap.<Integer, String>create();
		for (Object node : doc.getRootElement().elements(elementName)) {
			Element e = (Element) node;
			Integer id = Integer.valueOf(e.elementText("id"));
			String name = e.elementText("name");
			map.put(id, name);
		}
		
		return map;
	}
	
	public BiMap<Integer, String> getPriorities() {
		return getNamedEntities("/enumerations/issue_priorities.xml", "issue_priority");
	}
	
	public BiMap<Integer, String> getStatuses() {
		return getNamedEntities("/issue_statuses.xml", "issue_status");
	}
	
	public BiMap<Integer, String> getTrackers() {
		return getNamedEntities("/trackers.xml", "tracker");
	}
	
	public BiMap<Integer, String> getCustomFields() {
		return getNamedEntities("/custom_fields.xml", "custom_field");
	}
	
	public Integer findUserId(String name) {
		WebResource resource = resource().path("/users.xml").queryParam("name", name);
		ClientResponse response = resource.accept(MediaType.TEXT_XML, MediaType.APPLICATION_XML)
										  .get(ClientResponse.class);
		
		int status = response.getStatus();
		if (status == HttpStatus.SC_OK) {
			String xml = response.getEntity(String.class);
			if (Strings.isNullOrEmpty(xml)) {
				throw new TrackerException("Unable to find any user matching name like " + name);
			}
			
			Document doc = XMLHelper.readXML(xml);
			for (Object each : doc.getRootElement().elements("user")) {
				Element e = (Element) each;
				String login = e.elementText("login");
				if (Objects.equal(login, name)) {
					return Integer.valueOf(e.elementText("id"));
				}
			}
		}
			
		throw new TrackerException("Unable to find any user matching name like " + name);
	}
	
	WebResource resource() {
		return RestClientFactory.resource(host, username, password);
	}
}
