/**
 * <a href="http://www.openolat.org">
 * OpenOLAT - Online Learning and Training</a><br>
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); <br>
 * you may not use this file except in compliance with the License.<br>
 * You may obtain a copy of the License at the
 * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
 * <p>
 * Unless required by applicable law or agreed to in writing,<br>
 * software distributed under the License is distributed on an "AS IS" BASIS, <br>
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
 * See the License for the specific language governing permissions and <br>
 * limitations under the License.
 * <p>
 * Initial code contributed and copyrighted by<br>
 * frentix GmbH, http://www.frentix.com
 * <p>
 */
package org.olat.restapi.system;

import java.util.Calendar;
import java.util.List;
import java.util.Set;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.olat.admin.sysinfo.manager.SessionStatsManager;
import org.olat.admin.sysinfo.model.SessionsStats;
import org.olat.basesecurity.BaseSecurity;
import org.olat.basesecurity.Constants;
import org.olat.core.CoreSpringFactory;
import org.olat.core.id.Identity;
import org.olat.core.util.SessionInfo;
import org.olat.core.util.UserSession;
import org.olat.core.util.WorkThreadInformations;
import org.olat.core.util.session.UserSessionManager;
import org.olat.course.CourseModule;
import org.olat.group.BusinessGroupService;
import org.olat.portfolio.manager.InvitationDAO;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryManager;
import org.olat.restapi.system.vo.OpenOLATStatisticsVO;
import org.olat.restapi.system.vo.RepositoryStatisticsVO;
import org.olat.restapi.system.vo.SessionsVO;
import org.olat.restapi.system.vo.TasksVO;
import org.olat.restapi.system.vo.UserStatisticsVO;

/**
 * 
 * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
 */
public class OpenOLATStatisticsWebService implements Sampler {

	private final IndexerWebService indexerWebService = new IndexerWebService();

	/**
	 * Return the statistics about OpenOLAT, users count, courses count... 
	 * @response.representation.200.qname {http://www.example.com}releaseVO
	 * @response.representation.200.mediaType application/xml, application/json
	 * @response.representation.200.doc The verison of the instance
	 * @response.representation.200.example {@link org.olat.restapi.system.vo.Examples#SAMPLE_OO_STATSVO}
	 * @response.representation.401.doc The roles of the authenticated user are not sufficient
	 * @param request The HTTP request
	 * @return The statistics about OpenOLAT
	 */
	@GET
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Response getStatistics() {
		OpenOLATStatisticsVO stats = new OpenOLATStatisticsVO();
		stats.setSessions(getSessionsVO());
		stats.setUserStatistics(getUserStatisticsVO());
		stats.setRepositoryStatistics(getRepositoryStatisticsVO());
		stats.setIndexerStatistics(indexerWebService.getIndexerStatistics());
		return Response.ok(stats).build();
	}

	/**
	 * Return the statistics about OpenOLAT users
	 * @response.representation.200.qname {http://www.example.com}releaseVO
	 * @response.representation.200.mediaType application/xml, application/json
	 * @response.representation.200.doc The verison of the instance
	 * @response.representation.200.example {@link org.olat.restapi.system.vo.Examples#SAMPLE_OO_USERSSTATSVO}
	 * @response.representation.401.doc The roles of the authenticated user are not sufficient
	 * @param request The HTTP request
	 * @return The statistics about OpenOLAT users
	 */
	@GET
	@Path("users")
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Response getUserStatistics() {
		UserStatisticsVO stats = getUserStatisticsVO();
		return Response.ok(stats).build();
	}

	/**
	 * Return the statistics about the repository, courses count, published courses... 
	 * @response.representation.200.qname {http://www.example.com}releaseVO
	 * @response.representation.200.mediaType application/xml, application/json
	 * @response.representation.200.doc The verison of the instance
	 * @response.representation.200.example {@link org.olat.restapi.system.vo.Examples#SAMPLE_OO_REPOSTATSVO}
	 * @response.representation.401.doc The roles of the authenticated user are not sufficient
	 * @param request The HTTP request
	 * @return The statistics about the repository
	 */
	@GET
	@Path("repository")
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Response getRepositoryStatistics() {
		RepositoryStatisticsVO stats = getRepositoryStatisticsVO();
		return Response.ok(stats).build();
	}

	/**
	 * Return the statistics about the indexer
	 * @response.representation.200.qname {http://www.example.com}releaseVO
	 * @response.representation.200.mediaType application/xml, application/json
	 * @response.representation.200.doc The verison of the instance
	 * @response.representation.200.example {@link org.olat.restapi.system.vo.Examples#SAMPLE_OO_INDEXERSTATSVO}
	 * @response.representation.401.doc The roles of the authenticated user are not sufficient
	 * @param request The HTTP request
	 * @return The statistics about the indexer
	 */
	@Path("indexer")
	public IndexerWebService getIndexerStatistics() {
		return indexerWebService;
	}

	/**
	 * Return some statistics about session.
	 * @response.representation.200.qname {http://www.example.com}sessionVO
	 * @response.representation.200.mediaType application/xml, application/json
	 * @response.representation.200.doc A short summary about sessions
	 * @response.representation.200.example {@link org.olat.restapi.system.vo.Examples#SAMPLE_SESSIONVO}
	 * @response.representation.401.doc The roles of the authenticated user are not sufficient
	 * @param request The HTTP request
	 * @return The statistics about sessions
	 */
	@GET
	@Path("sessions")
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Response getSessions() {
		SessionsVO vo = getSessionsVO();
		return Response.ok(vo).build();
	}

	/**
	 * Return some statistics about long running tasks.
	 * @response.representation.200.qname {http://www.example.com}taskVOes
	 * @response.representation.200.mediaType application/xml, application/json
	 * @response.representation.200.doc A short summary about sessions
	 * @response.representation.200.example {@link org.olat.restapi.system.vo.Examples#SAMPLE_SESSIONVO}
	 * @response.representation.401.doc The roles of the authenticated user are not sufficient
	 * @param request The HTTP request
	 * @return The statistics about sessions
	 */
	@GET
	@Path("tasks")
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Response getTasks() {
		TasksVO vo = getTasksVO();
		return Response.ok(vo).build();
	}

	private UserStatisticsVO getUserStatisticsVO() {
		UserStatisticsVO stats = new UserStatisticsVO();
		BaseSecurity securityManager = CoreSpringFactory.getImpl(BaseSecurity.class);

		// activeUserCount="88" // registered and activated identities, same as in GUI
		long countActiveUsers = securityManager.countIdentitiesByPowerSearch(null, null, false, null, null, null, null, null, null, null, Constants.USERSTATUS_ACTIVE);
		stats.setActiveUserCount(countActiveUsers);

		// active last day
		Calendar lastDay = Calendar.getInstance();
		lastDay.add(Calendar.DATE, -1);
		long activeUserCountDay = securityManager.countUniqueUserLoginsSince(lastDay.getTime());
		stats.setActiveUserCountLastDay(activeUserCountDay);

		// active last week
		Calendar lastWeek = Calendar.getInstance();
		lastWeek.add(Calendar.DATE, -7);
		long activeUserCountWeek = securityManager.countUniqueUserLoginsSince(lastWeek.getTime());
		stats.setActiveUserCountLastWeek(activeUserCountWeek);

		// active last month
		Calendar lastMonth = Calendar.getInstance();
		lastMonth.add(Calendar.MONTH, -1);
		long activeUserCountMonth = securityManager.countUniqueUserLoginsSince(lastMonth.getTime());
		stats.setActiveUserCountLastMonth(activeUserCountMonth);

		// active last 6 month
		Calendar last6Month = Calendar.getInstance();
		last6Month.add(Calendar.MONTH, -6);
		long activeUserCount6Month = securityManager.countUniqueUserLoginsSince(last6Month.getTime());
		stats.setActiveUserCountLast6Month(activeUserCount6Month);

		// externalUserCount="12" // EP invite identities, later maybe also used in courses for MOOCS, external experts etc)
		long invitationsCount = CoreSpringFactory.getImpl(InvitationDAO.class).countInvitations();
		stats.setExternalUserCount(invitationsCount);

		// blockedUserCount="0" // identities in login blocked state
		long blockedUserCount = securityManager.countIdentitiesByPowerSearch(null, null, true, null, null, null, null, null, null, null, Identity.STATUS_LOGIN_DENIED);	
		stats.setBlockedUserCount(blockedUserCount);
		// deletedUserCount="943" // deleted identities
		long deletedUserCount = securityManager.countIdentitiesByPowerSearch(null, null, true, null, null, null, null, null, null, null, Identity.STATUS_DELETED);	
		stats.setDeletedUserCount(deletedUserCount);

		// totalUserCount="1043" // Sum of all above
		long countUsers = securityManager.countIdentitiesByPowerSearch(null, null, false, null, null, null, null, null, null, null, null);
		stats.setTotalUserCount(countUsers);

		BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
		long countGroups = bgs.countBusinessGroups(null, null);
		stats.setTotalGroupCount(countGroups);
		return stats;
	}

	private RepositoryStatisticsVO getRepositoryStatisticsVO() {
		RepositoryStatisticsVO stats = new RepositoryStatisticsVO();
		RepositoryManager repoMgr = CoreSpringFactory.getImpl(RepositoryManager.class);
		int allCourses = repoMgr.countByTypeLimitAccess(CourseModule.ORES_TYPE_COURSE, RepositoryEntry.ACC_OWNERS);
		int publishedCourses = repoMgr.countByTypeLimitAccess(CourseModule.ORES_TYPE_COURSE, RepositoryEntry.ACC_USERS);
		stats.setCoursesCount(allCourses);
		stats.setPublishedCoursesCount(publishedCourses);
		return stats;
	}

	private TasksVO getTasksVO() {
		TasksVO tasks = new TasksVO();
		List<String> longRunningTaskList = WorkThreadInformations.getLongRunningTasks();
		String[] longRunningTasks = longRunningTaskList.toArray(new String[longRunningTaskList.size()]);
		tasks.setLongRunningTasks(longRunningTasks);
		return tasks;
	}

	protected SessionsVO getSessionsVO() {
		SessionsVO vo = new SessionsVO();

		SessionStatsManager sessionStatsManager = CoreSpringFactory.getImpl(SessionStatsManager.class);
		UserSessionManager sessionManager = CoreSpringFactory.getImpl(UserSessionManager.class);
		vo.setCount(sessionManager.getUserSessionsCnt());

		Set<UserSession> userSessions = sessionManager.getAuthenticatedUserSessions();
		int webdavcount = 0;
		int secureWebdavCount = 0;
		int authenticatedcount = 0;
		int secureAuthenticatedCount = 0;
		int restCount = 0;
		int secureRestCount = 0;
		for (UserSession usess:userSessions) {
			SessionInfo sessInfo = usess.getSessionInfo();
			if (sessInfo.isWebDAV()) {
				webdavcount++;
				if (sessInfo.isSecure()) {
					secureWebdavCount++;
				}
			} else if (sessInfo.isREST()) {
				restCount++;
				if (sessInfo.isSecure()) {
					secureRestCount++;
				}
			} else {
				authenticatedcount++;
				if (sessInfo.isSecure()) {
					secureAuthenticatedCount++;
				}
			}
		}

		vo.setAuthenticatedCount(authenticatedcount);
		vo.setSecureAuthenticatedCount(secureAuthenticatedCount);
		vo.setWebdavCount(webdavcount);
		vo.setSecureWebdavCount(secureWebdavCount);
		vo.setRestCount(restCount);
		vo.setSecureRestCount(secureRestCount);
		//Instant messaging
		vo.setInstantMessagingCount(-1);

		SessionsStats statsLastMinute = sessionStatsManager.getSessionsStatsLast(60);
		SessionsStats statsLast5Minutes = sessionStatsManager.getSessionsStatsLast(300);
		vo.setAuthenticatedClickCountLastMinute(statsLastMinute.getAuthenticatedClickCalls());
		vo.setAuthenticatedClickCountLastFiveMinutes(statsLast5Minutes.getAuthenticatedPollerCalls());
		vo.setAuthenticatedPollCountLastMinute(statsLastMinute.getAuthenticatedPollerCalls());
		vo.setAuthenticatedPollCountLastFiveMinutes(statsLast5Minutes.getAuthenticatedPollerCalls());
		vo.setRequestLastMinute(statsLastMinute.getRequests());
		vo.setRequestLastFiveMinutes(statsLast5Minutes.getRequests());
		vo.setConcurrentDispatchThreads(sessionStatsManager.getConcurrentCounter());	
		return vo;
	}

	@Override
	public void takeSample() {
		SessionStatsManager manager = CoreSpringFactory.getImpl(SessionStatsManager.class);
		if(manager != null) {//check if the manager is loaded
			manager.takeSample();
		}
	}
}