package com.apobates.forum.trident.controller;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.letterbox.api.service.ForumLetterService;
import com.apobates.forum.letterbox.api.service.InboxService;
import com.apobates.forum.letterbox.api.service.OutboxService;
import com.apobates.forum.letterbox.entity.ForumLetter;
import com.apobates.forum.letterbox.entity.ForumLetterReceiver;
import com.apobates.forum.letterbox.entity.ForumLetterTypeEnum;
import com.apobates.forum.member.api.service.MemberService;
import com.apobates.forum.member.entity.Member;
import com.apobates.forum.member.storage.core.MemberSessionBean;
import com.apobates.forum.trident.OnlineDescriptor;
import com.apobates.forum.trident.digest.MessageBodyDigest;
import com.apobates.forum.trident.vo.MutualLetter;
import com.apobates.forum.trident.controller.form.ForumMessageForm;
import com.apobates.forum.utils.CommonBean;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.DateTimeUtils;
import com.apobates.forum.utils.FrontPageURL;
import com.apobates.forum.utils.TipMessage;
import com.apobates.forum.utils.lang.EnumArchitecture;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.PageRequest;
import com.apobates.forum.utils.persistence.Pageable;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
/**
 * 消息控制器
 * 
 * @author xiaofanku
 * @since 20190612
 */
@Controller
@RequestMapping(value = "/message")
public class MessageController {
	@Autowired
	private InboxService inboxService;
	@Autowired
	private OutboxService outboxService;
	@Autowired
	private ForumLetterService forumLetterService;
	@Autowired
	private MemberService memberService;
	@Value("${site.pageSize}")
	private int pageSize;
	private final static Logger logger = LoggerFactory.getLogger(MessageController.class);
	
	//与我通消息的会员[A]
	@GetMapping(path="/")
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_BOX)
	public String homePage(
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		//未读的消息数量
		model.addAttribute("unreadSize", inboxService.countForMemberMessages(mbean.getMid()));
		return "default/message/index";
	}
	@GetMapping(path="/inbox/rawdata", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public String inboxRawdata(
			@RequestParam("pageNumber") int page, 
			@RequestParam("pageSize")int pageSize, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		Page<ForumLetter> rs = inboxService.getInBoxGroupSender(mbean.getMid(), new PageRequest(page, pageSize));
		//
		Map<String,Object> result = new HashMap<>();
		result.put("result", rs.getResult().map(InboxClassVO::new).collect(Collectors.toList()));
		result.put("total", rs.getTotalElements());
		result.put("page", page);
		result.put("size", pageSize);
		Gson gson = new Gson();
		return gson.toJson(result);
	}
	
	// [A]{/message/}列表中未读消息数量
	@GetMapping(path="/size", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public List<CommonBean> getUnReadMessageSize(@RequestParam("sender")String senderIdString, MemberSessionBean mbean, HttpServletRequest request, Model model){
		Set<Long> senderIdSet = Commons.toLongSet(senderIdString);
		Map<Long, Long> rs = inboxService.groupForMemberMessages(mbean.getMid(), senderIdSet);
		//
		return rs.entrySet().stream().map((entry) -> new CommonBean(entry.getKey(), entry.getValue() + "")).collect(Collectors.toList());
	}
	
	//发件箱
	@GetMapping(path="/sent")
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_SENT)
	public String sendPage(
			@RequestParam(value = "type", required = false, defaultValue = "0") int typeEnumSymbol, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		ForumLetterTypeEnum label = EnumArchitecture.getInstance(typeEnumSymbol, ForumLetterTypeEnum.class).orElse(ForumLetterTypeEnum.ALL);
		long count = (ForumLetterTypeEnum.ALL == label)?outboxService.countSentByMember(mbean.getMid()):outboxService.countSentByMember(mbean.getMid(), label);
		model.addAttribute("total", Commons.optional(count, 0L));
		return "default/message/sent";
	}
	@GetMapping(path="/outbox/rawdata", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public String getMemberSendRawdata(
			@RequestParam(value = "type", required = false, defaultValue = "0") int typeEnumSymbol, 
			@RequestParam("pageNumber") int page, 
			@RequestParam("pageSize")int pageSize, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		ForumLetterTypeEnum label = EnumArchitecture.getInstance(typeEnumSymbol, ForumLetterTypeEnum.class).orElse(ForumLetterTypeEnum.ALL);
		Pageable pr = new PageRequest(page, pageSize);
		Page<ForumLetter> rs = (ForumLetterTypeEnum.ALL == label)?outboxService.getSent(mbean.getMid(), pr):outboxService.getSent(mbean.getMid(), label, pr);
		//
		Map<String,Object> result = new HashMap<>();
		result.put("result", rs.getResult().map(OutboxClassVO::new).collect(Collectors.toList()));
		result.put("total", rs.getTotalElements());
		result.put("page", page);
		result.put("size", pageSize);
		Gson gson = new Gson();
		return gson.toJson(result);
	}
	
	//新消息
	@GetMapping(path="/create")
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_CREATE)
	public String createForm(
			@RequestParam(value = "receiver", required = false, defaultValue = "0")long receiver, 
			@RequestParam(value = "names", required = false, defaultValue = "")String names, 
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		ForumMessageForm form = new ForumMessageForm();
		if(receiver>0){
			form.setUid("u"+receiver);
		}
		form.setSnames(names);
		form.setToken(token);
		model.addAttribute("form", form);
		return "default/message/create";
	}
	@PostMapping(path="/create")
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_CREATE)
	public String createAction(
			@ModelAttribute("form")ForumMessageForm form, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		Member m = memberService.get(form.getMemberId()).orElse(null);
		//
		if(null!=m && Commons.isNotBlank(form.getContent())){
			//发布消息
			String mt = Commons.isNotBlank(form.getTitle())?form.getTitle():mbean.getNickname()+"发来一封私信";
			long fmId = forumLetterService.create(mt, form.getContent(), m.getId(), m.getNickname(), mbean.getMid(), mbean.getNickname());
			if(fmId>0){
				return "redirect:/message/sent";
			}
			model.addAttribute("form", form);
			//
			model.addAttribute("errors", "发送失败, 建议您稍候重新尝试");
			return "default/message/create";
		}
		model.addAttribute("form", form);
		//
		model.addAttribute("errors", "发送失败, 建议您检查是否有漏填的输入");
		return "default/message/create";
	}
	
	//ajax站内信
	@PostMapping(path="/transmit", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_CREATE, isAjax=true)
	public MessageBodyDigest transmitMessageAction(
			@RequestParam("receiver")long receiver, 
			@RequestParam("names")String receiverNickname, 
			@RequestParam("content")String content, 
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		if(receiver<1){
			return MessageBodyDigest.ofEmpty("未知的消息接受者");
		}
		ForumLetter fm = forumLetterService.build(mbean.getNickname()+"发来一封私信", content.trim(), receiver, receiverNickname, mbean.getMid(), mbean.getNickname()).orElse(new ForumLetter());
		if(fm.getId()>0){
			return new MessageBodyDigest(fm, receiver);
		}
		return new MessageBodyDigest(mbean.getMid(), mbean.getNickname(), receiver, fm.getTitle(), fm.getContent());
	}
	
	//收件人将消息标为已读
	@PostMapping(path="/read", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_ACTION_READ, isAjax=true)
	public TipMessage readMessageAction(
			@RequestParam("ids")String idString, 
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		List<Long> messageIdList = Commons.toLongList(idString);
		return TipMessage.Builder.of(()->inboxService.readed(mbean.getMid(), messageIdList)>0).success("标记成功").error("标记为阅读操作失败");
	}
	
	//收件人将消息标为删除
	@PostMapping(path="/delete", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_ACTION_DEL, isAjax=true)
	public TipMessage deleteMessageAction(
			@RequestParam("ids")String idString, 
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		List<Long> messageIdList = Commons.toLongList(idString);
		return TipMessage.Builder.of(()->inboxService.remove(mbean.getMid(), messageIdList)>0).success("删除成功").error("删除消息操作失败");
	}
	
	//收件箱中的查看
	@GetMapping(path="/view")
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_BROWSE)
	public String showMessage(
			@RequestParam("sender")long sendMemberId, 
			@RequestParam(value = "pn", required = false, defaultValue = "5") int pageSize, 
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		//我与发件人来往的信件
		FrontPageURL fpbuild = new FrontPageURL(request.getContextPath() + "/message/view").addPageSize("number", pageSize).addParameter("sender", sendMemberId);
		//打开这个页面永远只显示第一页,此页面无分页区
		Pageable pr = new PageRequest(1, fpbuild.getPageSize());
		//保证是降序
		Page<ForumLetter> data = forumLetterService.getMutualLetter(sendMemberId, mbean.getMid(), pr);
		//jsp中foreach,需要将之重排成升序
		List<ForumLetter> rs = data.getResult().sorted(Comparator.comparing(ForumLetter::getEntryDateTime)).collect(Collectors.toList());
		//主在右，客在左
		model.addAttribute("rs", rs);
		model.addAttribute("master", mbean.toMember());
		//
		boolean hasMoreRecords = data.getTotalElements() > pageSize;
		model.addAttribute("hasMore", hasMoreRecords);
		//可能这一页只有主,没有发送者信息
		Member senderMember = memberService.get(sendMemberId).orElse(Member.empty(sendMemberId));
		model.addAttribute("senderNickname", senderMember.getNickname()); 
		
		model.addAttribute("sender", sendMemberId);
		model.addAttribute("token", token); //Commons.randomAlphaNumeric(8)
		return "default/message/inbox_chat"; //
	}
	
	@GetMapping(path="/view/more", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public String getMutualHistoryLetter(
			@RequestParam("sender")long sendMemberId, 
			@RequestParam("p") int page, 
			@RequestParam(value = "pn", required = false, defaultValue = "5") int pageSize, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		Pageable pr = new PageRequest(page, pageSize); 
		Page<ForumLetter> data = forumLetterService.getMutualLetter(sendMemberId, mbean.getMid(), pr);
		//保证是降序,push到js中是第一个先填充
		List<MessageBodyDigest> entries = data.getResult()
												.map(fl ->{
													boolean isMaster = fl.getAuthor() == mbean.getMid(); //信的作者=当前用户
													long receiverMember = (isMaster)?sendMemberId:mbean.getMid();
													return new MessageBodyDigest(fl, receiverMember, isMaster);
												})
												.collect(Collectors.toList());
		//
		boolean hasMoreRecords = data.getTotalElements() > (page * pageSize);
		MutualLetter ml = new MutualLetter(sendMemberId, mbean.getMid(), page, pageSize, hasMoreRecords);
		//
		Gson gson = new Gson();
		Type setType = new TypeToken<TreeSet<MessageBodyDigest>>() {}.getType();
		JsonObject jsonObject = new JsonObject();
		jsonObject.addProperty("letter", gson.toJson(ml));
		jsonObject.add("result", gson.toJsonTree(entries, setType));
		return jsonObject.toString();
	}
	
	//全部标记为已读
	//将发件人发送的消息全部标记为阅读
	@PostMapping(path="/read/all", produces = "application/json;charset=UTF-8")
	@ResponseBody
	@OnlineDescriptor(action=ForumActionEnum.MESSAGE_ACTION_READ, isAjax=true)
	public TipMessage readAllMessageAction(
			@RequestParam("member")long memberId, 
			@RequestParam("direction")int direction, 
			@RequestParam(value = "token", required = false, defaultValue = "0")String token, 
			MemberSessionBean mbean, 
			HttpServletRequest request, 
			Model model){
		final long othMember =(direction == 1)?memberId:-1;
		return TipMessage.Builder.take(()->inboxService.compositeReaded(mbean.getMid(), othMember, direction)).success("标记成功").error("标记为阅读操作失败");
	}
	
	//今天的公告
	@GetMapping(path="/notice", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public List<Map<String,String>> getTodayClaimLetter(
			HttpServletRequest request, 
			Model model){
		Function<ForumLetter, Map<String, String>> mapper = fl -> {
			Map<String, String> tmp = new HashMap<>();
			tmp.put("title", fl.getTitle());
			tmp.put("content", fl.getContent());
			tmp.put("author", fl.getAuthor() + "");
			tmp.put("nickname", fl.getNickname());
			tmp.put("pubdate", DateTimeUtils.formatClock(fl.getEntryDateTime()));
			return tmp;
		};
		return forumLetterService.getTodayClaim().map(mapper).collect(Collectors.toList());
	}
	class InboxClassVO{
		private final String subject;
		private final long sender;
		private final String senderNickname;
		private final String senderURI;
		private final String datetime;
		private final boolean robot;
		
		public InboxClassVO(ForumLetter letter){
			this.subject = letter.getTitle();
			this.sender = letter.getAuthor();
			this.senderNickname  = letter.getNickname();
			if(0 == letter.getAuthor()){
				this.senderURI = "";
				this.robot = true;
			}else{
				this.senderURI = String.format("/member/%d.xhtml", letter.getAuthor());
				this.robot = false;
			}
			this.datetime = DateTimeUtils.formatClock(letter.getEntryDateTime());
		}
		
		public String getSubject() {
			return subject;
		}
		public long getSender() {
			return sender;
		}
		public String getSenderNickname() {
			return senderNickname;
		}
		public String getSenderURI() {
			return senderURI;
		}
		public String getDatetime() {
			return datetime;
		}
		public boolean isRobot() {
			return robot;
		}
	}
	class OutboxClassVO{
		private final String subject;
		private final String datetime;
		private final String content;
		private final String category;
		private final List<Map<String,String>> receives;
		private final boolean everyone;
		//多个中的一位
		private final long receiver;
		private final String receiverURI;
		private final String receiverNickname;
		
		public OutboxClassVO(ForumLetter letter){
			this.subject = letter.getTitle();
			this.content = letter.getContent();
			this.datetime = DateTimeUtils.formatClock(letter.getEntryDateTime());
			this.category = letter.getTyped().getTitle();
			//
			List<Map<String,String>> reces = new ArrayList<>();
			boolean isEveryOne = false;
			for(ForumLetterReceiver flr : letter.getReceivers()){
				Map<String,String> tmp = new HashMap<>();
				tmp.put("nickname", flr.getMemberNickname());
				tmp.put("id", flr.getMember()+"");
				reces.add(tmp);
				if(!isEveryOne && 0L == flr.getMember()){
					isEveryOne = true;
				}
			}
			this.receives = reces;
			this.everyone = isEveryOne;
			//
			this.receiver = Long.valueOf(reces.get(0).get("id"));
			this.receiverNickname = reces.get(0).get("nickname");
			this.receiverURI = String.format("/member/%d.xhtml", receiver);
		}
		
		public String getSubject() {
			return subject;
		}
		public String getDatetime() {
			return datetime;
		}
		public String getContent() {
			return content;
		}
		public String getCategory() {
			return category;
		}
		public List<Map<String, String>> getReceives() {
			return receives;
		}
		public boolean isEveryone() {
			return everyone;
		}
		public long getReceiver() {
			return receiver;
		}
		public String getReceiverURI() {
			return receiverURI;
		}
		public String getReceiverNickname() {
			return receiverNickname;
		}
		
	}
}
