/**
 * Tigase AuditLog Component - Implementation of Audit Log pattern for Tigase XMPP Server
 * Copyright (C) 2017 Tigase, Inc. (office@tigase.com) - All Rights Reserved
 * Unauthorized copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential
 */
/*
 Get AuditLog list of disconnected users

 AS:Description: Get disconnected users
 AS:CommandId: get-disconnected-users
 AS:Component: audit-log
 AS:Group: Audit Log
 */

package tigase.admin

import groovy.transform.CompileStatic
import tigase.auditlog.AuditLogComponent
import tigase.server.Command
import tigase.server.Iq
import tigase.server.Packet
import tigase.util.datetime.TimestampHelper
import tigase.vhosts.VHostManagerIfc
import tigase.xml.Element

import java.sql.Timestamp
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.stream.Collectors

AuditLogComponent component = (AuditLogComponent) component
VHostManagerIfc vhost_man = (VHostManagerIfc) vhostMan
packet = (Iq) packet

@CompileStatic
String formatDuration(Date ts, Double duration) {
	if (duration == null) {
		return null;
	}

	if (ts == null) {
		ts = new Date();
	}

	Instant from = ts.toInstant();
	Instant to = Instant.ofEpochMilli(ts.getTime() + ((long)(duration * 1000)));

	StringBuilder sb = new StringBuilder();
	long amount = from.until(to, ChronoUnit.DAYS);
	if (amount) {
		from = from.plus(amount, ChronoUnit.DAYS);
		if (sb.length() > 0) {
			sb.append(" ");
		}
		sb.append(amount).append("d");
	}
	amount = from.until(to, ChronoUnit.HOURS);
	if (amount) {
		from = from.plus(amount, ChronoUnit.HOURS);
		if (sb.length() > 0) {
			sb.append(" ");
		}
		sb.append(amount).append("h");
	}
	amount = from.until(to, ChronoUnit.MINUTES);
	if (amount) {
		from = from.plus(amount, ChronoUnit.MINUTES);
		if (sb.length() > 0) {
			sb.append(" ");
		}
		sb.append(amount).append("m");
	}
	amount = from.until(to, ChronoUnit.SECONDS);
	if (amount) {
		from = from.plus(amount, ChronoUnit.SECONDS);
		if (sb.length() > 0) {
			sb.append(" ");
		}
		sb.append(amount).append("s");
	}
	amount = from.until(to, ChronoUnit.MILLIS);
	if (amount) {
		from = from.plus(amount, ChronoUnit.MILLIS);
		if (sb.length() > 0) {
			sb.append(" ");
		}
		sb.append(amount).append("ms");
	}
	if (sb.length() == 0) {
		return "0ms";
	}
	return sb.toString();
}

@CompileStatic
Element createDurationValueElem(Date ts, Double duration) {
	Element value = new Element("value", duration != null ? String.format("%.3f", duration) : "");
	if (duration != null) {
		value.addAttribute("label", formatDuration(ts, duration));
	}
	return value;
}

@CompileStatic
Element createTimestampValueElement(TimestampHelper timestampHelper, Date ts) {
	Element value = new Element("value", ts ? timestampHelper.formatWithMs(ts) : "");
	if (ts != null) {
		value.addAttribute("label", "" + new java.sql.Timestamp(ts.getTime()));
	}
	return value;
}

@CompileStatic
Packet process(AuditLogComponent component, Iq p, VHostManagerIfc vHostManager) {
	String JID_LIKE = "jidLike";
	String DOMAIN = "domain";

	TimestampHelper timestampHelper = new TimestampHelper();

	String domain = Command.getFieldValue(p, DOMAIN);
	String jidLike = Command.getFieldValue(p, JID_LIKE);

	Packet result = p.commandResult(Command.DataType.form);

	Command.addTitle(result, "Querying auditlog repository.")
	Command.addInstructions(result, "Fill out this form to query auditlog repository.")

	List<String> vhosts = vHostManager.getAllVHosts().stream().map({ j -> j.toString() }).collect(Collectors.toList());
	Command.addFieldValue(result, DOMAIN, domain ?: "",
						  "Domain", vhosts as String[], vhosts as String[]);
	Command.addFieldValue(result, JID_LIKE, jidLike ?: "", "text-single",
						  "Filter")

	if (domain != null) {
		if (jidLike != null && jidLike.isEmpty()) {
			jidLike = null;
		}
		Element reported = new Element("reported");
		reported.addAttribute("label", "Disconnected users");
		def cols = [ "JID", "Status", "Disconnected from", "Duration" ];
		cols.each {
			Element el = new Element("field");
			el.setAttribute("var", it);
			if (it == "Duration") {
				el.setAttribute("align", "right");
			}
			reported.addChild(el);
		}
		result.getElement().getChild('command').getChild('x').addChild(reported);

		component.getSearchableRepository().getDisconnectedUsers(domain, jidLike).each {
			Element item = new Element("item");

			Element jid = new Element("field");
			jid.setAttribute("var", "JID");
			jid.addChild(new Element("value", "" + it.userJID.toString()));
			item.addChild(jid);

			Element status = new Element("field");
			status.setAttribute("var", "Status");
			status.addChild(new Element("value", "Disconnected"));
			item.addChild(status);

			Element from = new Element("field");
			from.setAttribute("var", "Disconnected from");
			from.addChild(createTimestampValueElement(timestampHelper, it.getFrom()));
			item.addChild(from);

			Element duration = new Element("field");
			duration.setAttribute("var", "Duration");
			duration.addChild(createDurationValueElem(it.getFrom(), it.getDuration()));
			item.addChild(duration);

			result.getElement().getChild('command').getChild('x').addChild(item);
		}
	}

	return result
}

try {
	return process(component, packet, vhost_man)
} catch (Throwable ex) {
	ex.printStackTrace();
	throw ex;
}