/**
 * 
 */
package de.bstly.board.controller;

import java.time.Instant;
import java.time.temporal.ChronoUnit;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import de.bstly.board.businesslogic.EntryManager;
import de.bstly.board.model.Comment;
import de.bstly.board.model.Entry;
import de.bstly.board.model.LocalUser;
import de.bstly.board.model.QLocalUser;
import de.bstly.board.model.Vote;
import de.bstly.board.model.support.EntryStatus;
import de.bstly.board.model.support.EntryType;
import de.bstly.board.model.support.Types;
import de.bstly.board.model.support.VoteType;
import de.bstly.board.repository.CommentRepository;
import de.bstly.board.repository.LocalUserRepository;
import de.bstly.board.repository.VoteRepository;

/**
 * The Class DebugController.
 */
@RestController
@RequestMapping("/debug")
public class DebugController extends BaseController {

	private Logger logger = LogManager.getLogger(DebugController.class);

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private LocalUserRepository localUserRepository;

	@Autowired
	private CommentRepository commentRepository;

	@Autowired
	private VoteRepository voteRepository;

	@Autowired
	private EntryManager entryManager;

	@Value("${debug.random.users:0}")
	private int users;

	@Value("${debug.random.minEntries:0}")
	private int minEntries;

	@Value("${debug.random.maxEntries:10}")
	private int maxEntries;

	@Value("${debug.random.entryAge:63115200}")
	private long entryAge;

	@Value("${debug.random.minComments:0}")
	private int minComments;

	@Value("${debug.random.maxComments:10}")
	private int maxComments;

	@Value("${debug.random.subCommentsFactor:0.5}")
	private float subCommentsFactor;

	@Value("${debug.random.subCommentsThresh:0.3}")
	private float subCommentsThresh;

	@Value("${debug.random.subCommentsDepth:2}")
	private int subCommentsDepth;

	@Value("${debug.random.minUpvotes:5}")
	private int minUpvotes;

	@Value("${debug.random.maxUpvotes:10}")
	private int maxUpvotes;

	@Value("${debug.random.minDownvotes:0}")
	private int minDownvotes;

	@Value("${debug.random.maxDownvotes:10}")
	private int maxDownvotes;

	/**
	 * Random.
	 */
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@GetMapping("/random")
	public void random() {
		logger.warn("start random generation");

		long userCount = localUserRepository
				.count(QLocalUser.localUser.username.startsWith("user"));

		for (long i = userCount; i < userCount + users; i++) {
			LocalUser localUser = new LocalUser();
			String username = "user"
					+ i;
			localUser.setUsername(username);
			localUser.setPasswordHash(passwordEncoder.encode(username));
			localUserRepository.save(localUser);
			logger.trace("Created user: '"
					+ username
					+ "'");
		}

		logger.info("Created "
				+ users
				+ " users");

		for (long id = 0; id <= userCount; id++) {
			entries("user"
					+ id, userCount);
		}

		logger.warn("finished random generation");
	}

	/**
	 * Entries.
	 *
	 * @param username  the username
	 * @param userCount the user count
	 */
	protected void entries(String username, long userCount) {
		long numEntries = RandomUtils.nextLong(minEntries, maxEntries);
		for (int i = 0; i < numEntries; i++) {
			Entry entry = new Entry();
			entry.setEntryType(EntryType.INTERN);
			entry.setAuthor(username);
			entry.setCreated(
					Instant.now().minus(RandomUtils.nextLong(0, entryAge), ChronoUnit.SECONDS));
			entry.setTitle(RandomStringUtils.randomAscii(RandomUtils.nextInt(10, 250)));
			entry.setText(RandomStringUtils.randomAscii(RandomUtils.nextInt(0, 2500)));
			entry.setEntryStatus(EntryStatus.NORMAL);
			entry = entryManager.save(entry);
			logger.trace("Created entry: '"
					+ entry.getId()
					+ "'");
			comments(entry.getId(), entry.getCreated(), userCount);
			votes(entry.getId(), Types.entry, userCount);
		}
		logger.info("Created "
				+ numEntries
				+ " entries of '"
				+ username
				+ "'");
	}

	/**
	 * Comments.
	 *
	 * @param target    the target
	 * @param date      the date
	 * @param userCount the user count
	 */
	protected void comments(Long target, Instant date, long userCount) {
		long numComments = RandomUtils.nextLong(minComments, maxComments);
		logger.debug("Create "
				+ numComments
				+ " comments for '"
				+ target
				+ "'");
		for (int i = 0; i < numComments; i++) {
			Comment comment = new Comment();
			comment.setTarget(target);
			comment.setAuthor("user"
					+ RandomUtils.nextLong(0, userCount));
			comment.setText(RandomStringUtils.randomAscii(RandomUtils.nextInt(0, 2500)));
			comment.setCreated(Instant.now()
					.minus(RandomUtils.nextLong(0,
							(Instant.now().toEpochMilli() - date.toEpochMilli()) / 1000),
							ChronoUnit.SECONDS));
			comment = commentRepository.save(comment);
			logger.trace("Created comment: '"
					+ comment.getId()
					+ "'");
			subComments(target, comment.getId(), comment.getCreated(), subCommentsFactor,
					subCommentsThresh, 0, userCount);
		}
	}

	/**
	 * Sub comments.
	 *
	 * @param target    the target
	 * @param parent    the parent
	 * @param date      the date
	 * @param factor    the factor
	 * @param thresh    the thresh
	 * @param depth     the depth
	 * @param userCount the user count
	 */
	protected void subComments(Long target, Long parent, Instant date, float factor, float thresh,
			int depth, long userCount) {
		if (depth < subCommentsDepth && RandomUtils.nextFloat(0, 1) < thresh) {
			long numSubComments = RandomUtils.nextLong(0, Math.round(maxComments * factor));
			logger.debug("Create "
					+ numSubComments
					+ " subComments for '"
					+ parent
					+ "'");
			for (int i = 0; i < numSubComments; i++) {
				Comment comment = new Comment();
				comment.setTarget(target);
				comment.setParent(parent);
				comment.setAuthor("user"
						+ RandomUtils.nextLong(0, userCount));
				comment.setText(RandomStringUtils.randomAscii(RandomUtils.nextInt(0, 2500)));
				comment.setCreated(Instant.now()
						.minus(RandomUtils.nextLong(0,
								(Instant.now().toEpochMilli() - date.toEpochMilli()) / 1000),
								ChronoUnit.SECONDS));
				comment = commentRepository.save(comment);
				logger.trace("Created subComment: '"
						+ comment.getId()
						+ "'");
				subComments(target, comment.getId(), comment.getCreated(), factor * 0.5f,
						thresh * 0.5f, depth++, userCount);
			}
		}
	}

	/**
	 * Votes.
	 *
	 * @param target     the target
	 * @param targetType the target type
	 * @param userCount  the user count
	 */
	protected void votes(Long target, Types targetType, long userCount) {
		long numUpvotes = RandomUtils.nextLong(minUpvotes, maxUpvotes);
		logger.debug("Create "
				+ numUpvotes
				+ " upvotes for '"
				+ target
				+ "'");
		for (int i = 0; i < numUpvotes; i++) {
			Vote upvote = new Vote();
			upvote.setTarget(target);
			upvote.setType(VoteType.up);
			upvote.setTargetType(targetType);
			upvote.setAuthor("user"
					+ RandomUtils.nextLong(0, userCount));
			upvote = voteRepository.save(upvote);
			logger.trace("Created upvote: '"
					+ upvote.getId()
					+ "'");
		}

		long numDownvotes = RandomUtils.nextLong(minDownvotes, maxDownvotes);
		logger.debug("Create "
				+ numDownvotes
				+ " downvotes for '"
				+ target
				+ "'");
		for (int i = 0; i < numDownvotes; i++) {
			Vote downvote = new Vote();
			downvote.setTarget(target);
			downvote.setType(VoteType.down);
			downvote.setTargetType(targetType);
			downvote.setAuthor("user"
					+ RandomUtils.nextLong(0, userCount));
			downvote = voteRepository.save(downvote);
			logger.trace("Created downvote: '"
					+ downvote.getId()
					+ "'");
		}
	}

}
