package org.springblade.modules.member.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.secure.provider.IClientDetails;
import org.springblade.core.secure.provider.IClientDetailsService;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.member.entity.MemberAccountEntity;
import org.springblade.modules.member.entity.MemberEntity;
import org.springblade.modules.member.enums.AccountTypeEnum;
import org.springblade.modules.member.mapper.MemberAccountMapper;
import org.springblade.modules.member.service.IMemberAccountService;
import org.springblade.modules.member.service.IMemberInfoService;
import org.springblade.modules.member.util.WXTokenUtil;
import org.springblade.modules.member.vo.MemberVO;
import org.springblade.modules.member.wx.config.WxMaConfiguration;
import org.springblade.modules.member.wx.dto.LoginTokenDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * 服务实现类
 *
 * @author Chill
 */
@Service
@AllArgsConstructor
public class MemberAccountServiceImpl extends BaseServiceImpl<MemberAccountMapper, MemberAccountEntity> implements IMemberAccountService {

	private IMemberInfoService memberInfoService;

	private IClientDetailsService clientDetailsService;

	@Override
	public Kv doWxMiniappRegister(String clientCode, String code) {
		return null;
	}

	@Override
	@Transactional(
		rollbackFor = {Exception.class}
	)
	public Kv doWxMiniappLogin(String clientCode, String code, String tenantId) {

		WxMaJscode2SessionResult session = this.getWxSession(clientCode, code);

		Kv ret = this.SignIn(AccountTypeEnum.WX_MINI_APP.getCode(), clientCode, session.getOpenid(), session.getUnionid(), tenantId);
		if (Func.isNull(ret)) {
			ret = this.signUp(AccountTypeEnum.WX_MINI_APP.getCode(), clientCode, session.getOpenid(), session.getUnionid(), tenantId);
		}

		return ret;
	}

	//登录
	private Kv SignIn(String accountType, String clientCode, String account, String appendAccount, String tenantId) {
		MemberAccountEntity accountEntity = this.getMemberAccount(accountType, account, appendAccount, tenantId);

		//新用户，由注册流程返回信息
		if (Func.isEmpty(accountEntity)) {
			return null;
		}

		Long memberId = accountEntity.getMemberId();
		String effective = "";

		MemberEntity member = memberInfoService.getById(memberId);

		LoginTokenDTO loginToken = this.getLoginToken(member, accountEntity.getAccount());


		return this.getAuthInfo(loginToken, effective);
	}

	//注册
	private Kv signUp(String accountType, String clientCode, String account, String appendAccount, String tenantId) {
		//1. 写member表
		MemberEntity member = memberInfoService.addMember(clientCode, tenantId);

		//2. 写member_account表
		MemberAccountEntity accountEntity = this.addAccount(member.getId(), accountType, account, appendAccount, tenantId);


		Long memberId = accountEntity.getMemberId();
		String effective = "";

		LoginTokenDTO loginToken = this.getLoginToken(member, accountEntity.getAccount());

		return this.getAuthInfo(loginToken, effective);
	}

	//TODO chenjs 改造返回前端的内容，看看是否有必要返回account
	private LoginTokenDTO getLoginToken(MemberEntity member, String account) {
		LoginTokenDTO loginToken = new LoginTokenDTO();

		MemberVO memberVO = Func.copyProperties(member, MemberVO.class);

		loginToken.setId(member.getId());
		loginToken.setAccount(account);
		loginToken.setAvator(member.getAvator());
		loginToken.setRealname(member.getRealName());
		loginToken.setNickName(member.getNickName());
		loginToken.setStatus(member.getStatus());
		loginToken.setMemberInfo(memberVO);
		loginToken.setTenantId(memberVO.getTenantId());

		return loginToken;
	}

	//获取微信会话
	private WxMaJscode2SessionResult getWxSession(String clientCode, String code) {
		try {
			IClientDetails clientDetail = clientDetailsService.loadClientByClientId(clientCode);
			if(Func.isEmpty(clientDetail)) {
				throw new ServiceException("应用id不存在");
			}
			final WxMaService wxService = WxMaConfiguration.getMaService();
			return wxService.getUserService().getSessionInfo(code);
		} catch (WxErrorException e) {
			throw new ServiceException("微信登录失败" + e.getMessage());
		}
	}


	//找到现有的会员账号
	private MemberAccountEntity getMemberAccount(String accountType, String account, String appendAccount, String tenantId) {
		//先用unionId找账号
		MemberAccountEntity accountEntity = this.getOne(Wrappers.<MemberAccountEntity>lambdaQuery()
			.eq(MemberAccountEntity::getTenantId, tenantId)
			.eq(MemberAccountEntity::getAccountType, accountType)
			.eq(MemberAccountEntity::getAppendAccount, appendAccount)
			.last("limit 1"));

		if (Func.isNotEmpty(accountEntity)) {
			return accountEntity;
		}

		//用unionId找不到账号之后再用openId找，因为第一版的用户只有openId
		//TODO chenjs 目前还有22个2018年5月份进入小程序的用户没有重新登录新版本，下次检查一下，如果全部已经重新登录了就改造这个方法
		accountEntity = this.getOne(Wrappers.<MemberAccountEntity>lambdaQuery()
			.eq(MemberAccountEntity::getAccountType, accountType)
			.eq(MemberAccountEntity::getAccount, account)
			.last("limit 1"));

		//如果找到了账号就将unionId写进去，下一次不再通过openId找
		if (Func.isNotEmpty(accountEntity)) {
			accountEntity.setAppendAccount(appendAccount);
			this.updateById(accountEntity);
		}

		return accountEntity;
	}


	private MemberAccountEntity addAccount(Long memberId, String accountType, String account, String appendAccount, String tenantId) {

		MemberAccountEntity accountEntity = new MemberAccountEntity();
		accountEntity.setAccount(account);
		accountEntity.setAppendAccount(appendAccount);
		accountEntity.setAccountType(accountType);
		accountEntity.setIsEnabled(Boolean.TRUE);
		accountEntity.setMemberId(memberId);
		accountEntity.setTenantId(tenantId);
		this.save(accountEntity);

		return accountEntity;
	}

	private Date getEffectiveDate(Date oldDate, Integer effectiveDays) {
		Date newDate = DateUtil.now();
		if (Func.isEmpty(oldDate)) {
			oldDate = DateUtil.now();
		}
		if (newDate.after(oldDate)) {
			return DateUtil.plusDays(newDate, effectiveDays);
		} else {
			return DateUtil.plusDays(oldDate, effectiveDays);
		}
	}

	private Kv getAuthInfo(LoginTokenDTO loginToken, String effective) {
		Kv kv = WXTokenUtil.createAuthInfo(loginToken);

		kv.set("tokenExpire", DateUtil.plusSeconds(DateUtil.now(), kv.getLong(TokenConstant.EXPIRES_IN)));
		kv.set("equityEffective", effective);

		return kv;
	}
}
