/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.gateway.shop_gateway.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.springblade.common.cache.PlatformCache;
import org.springblade.common.config.entity.PlatformConfig;
import org.springblade.common.enums.*;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.flow.shop.entity.ProcessAudit;
import org.springblade.flow.shop.service.impl.AuditShopService;
import org.springblade.gateway.shop_gateway.entity.dto.ShopAuditDto;
import org.springblade.gateway.shop_gateway.entity.vo.BalanceAccountInfoVO;
import org.springblade.gateway.shop_gateway.service.IAppShopService;
import org.springblade.ldt.account.entity.Account;
import org.springblade.ldt.account.service.IAccountService;
import org.springblade.ldt.bills.entity.TransferRec;
import org.springblade.ldt.bills.entity.WithdrawRec;
import org.springblade.ldt.bills.service.ITransferRecService;
import org.springblade.ldt.bills.service.IWithdrawRecService;
import org.springblade.ldt.mall.entity.Mall;
import org.springblade.ldt.mall.service.IMallService;
import org.springblade.ldt.notice.entity.NoticeManagement;
import org.springblade.ldt.notice.entity.NoticeManagementContent;
import org.springblade.ldt.notice.service.INoticeManagementContentService;
import org.springblade.ldt.notice.service.INoticeManagementService;
import org.springblade.ldt.shop.entity.Audit;
import org.springblade.ldt.shop.entity.Shop;
import org.springblade.ldt.shop.mapper.ShopMapper;
import org.springblade.ldt.shop.service.IAuditService;
import org.springblade.ldt.user.entity.LoginUser;
import org.springblade.ldt.user.entity.Member;
import org.springblade.ldt.user.service.ILoginUserService;
import org.springblade.ldt.user.service.IMemberService;
import org.springblade.wx.config.ConfigForClient;
import org.springblade.wx.config.ConfigForMall;
import org.springblade.wx.config.ConfigForOfficial;
import org.springblade.wx.config.ConfigForShop;
import org.springblade.yeePay.common.YeePayConst;
import org.springblade.yeePay.common.YeepayApiConstant;
import org.springblade.yeePay.entity.ConfigAppDto;
import org.springblade.yeePay.entity.saas.settlement.SettleBalanceQueryDto;
import org.springblade.yeePay.service.YeepayCommonService;
import org.springblade.yeePay.service.YeepaySaasService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;

/**
 * 服务实现类
 *
 * @author BladeX
 * @since 2021-08-30
 */
@Service
@AllArgsConstructor
public class AppShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IAppShopService {

	private AuditShopService auditShopService;
	private IAuditService auditService;
	private ILoginUserService loginUserService;
	private IMallService mallService;
	private YeepayCommonService yeepayCommonService;
	private YeePayConst yeePayConst;
	private ConfigForClient configForClient;
	private ConfigForMall configForMall;
	private ConfigForShop configForShop;
	private ConfigForOfficial configForOfficial;
	private IWithdrawRecService withdrawRecService;
	private ITransferRecService transferRecService;
	private YeepaySaasService yeepaySaasService;
	private IAccountService accountService;
	private IMemberService memberService;
	private INoticeManagementContentService noticeContentService;
	private INoticeManagementService noticeManagementService;


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void submitAudit(ShopAuditDto shopAuditDto) {
		Shop shop = shopAuditDto.getShop();
		Audit audit = shopAuditDto.getAudit();
		Long mallId = shop.getMallId();
		Mall mall = mallService.getById(mallId);
		if (!Objects.isNull(mall)) {
			String tenantId = mall.getTenantId();
			shop.setTenantId(tenantId);
			audit.setTenantId(tenantId);
		} else {
			shop.setTenantId(AppConstant.PLATFORM_TENANT_ID);
			audit.setTenantId(AppConstant.PLATFORM_TENANT_ID);
		}
		boolean isEdit = shop.getId() != null;

		if (isEdit) {
			//修改商户
			this.updateById(shop);
			auditService.updateById(audit);
			return;
		}
		//新增商户
		this.handelPromote(shop);
		shop.setCreateTime(new Date());
		this.save(shop);
		//新增的商户资料才开启审核流程
		audit.setEntityId(shop.getId());
		ProcessAudit processAudit = ProcessAudit.builder()
			.audit(audit)
			.build();
		auditShopService.startProcess(processAudit);
	}

	@Override
	public String getTenantId(Long shopId) {
		Shop shop = this.getById(shopId);
		if (shop == null) {
			return AppConstant.PLATFORM_TENANT_ID;
		}
		Long mallId = shop.getMallId();
		if (mallId == null) {
			return AppConstant.PLATFORM_TENANT_ID;
		}
		Mall mall = mallService.getById(mallId);
		String tenantId = Optional.ofNullable(mall).map(Mall::getTenantId).orElse(AppConstant.PLATFORM_TENANT_ID);
		return tenantId;
	}

	/**
	 * 配置商户的小程序信息（易宝报备商户信息）
	 *
	 * @param shop
	 * @return
	 */
	@Override
	public R configApp(Shop shop) {
		ConfigAppDto configAppDto = ConfigAppDto.builder()
			.merchantNo(shop.getMerchantNo())
			.parentMerchantNo(yeePayConst.getPlatformServiceNo())
			.build();

		ConfigAppDto.AppInfo clientApp = new ConfigAppDto.AppInfo();
		clientApp.setAppId(configForClient.getAppId());
		clientApp.setAppSecret(configForClient.getAppSecret());
		clientApp.setAppIdType("MINI_PROGRAM");

		ConfigAppDto.AppInfo shopApp = new ConfigAppDto.AppInfo();
		shopApp.setAppIdType("MINI_PROGRAM");
		shopApp.setAppId(configForShop.getAppId());
		shopApp.setAppSecret(configForShop.getAppSecret());

		ConfigAppDto.AppInfo mallApp = new ConfigAppDto.AppInfo();
		mallApp.setAppSecret(configForMall.getAppSecret());
		mallApp.setAppId(configForMall.getAppId());
		mallApp.setAppIdType("MINI_PROGRAM");

		ConfigAppDto.AppInfo official = new ConfigAppDto.AppInfo();
		official.setAppSecret(configForOfficial.getAppSecret());
		official.setAppId(configForOfficial.getAppId());
		official.setAppIdType("OFFICIAL_ACCOUNT");

		configAppDto.setAppIdList(new ConfigAppDto.AppInfo[]{clientApp, shopApp, mallApp, official});
		configAppDto.setTradeAuthDirList(configForOfficial.getTradeAuthDirList());

		return yeepayCommonService.configApp(configAppDto);
	}

	@Override
	public Long userBalanceWithdraw(WithdrawRec withdrawRec) {
		if (ObjectUtils.isEmpty(withdrawRec) || ObjectUtils.isEmpty(withdrawRec.getOwnerId())) {
			throw new ServiceException(ResultCode.PARAM_MISS);
		}
		Shop shop = getById(withdrawRec.getOwnerId());
		if (ObjectUtils.isEmpty(shop)) {
			throw new ServiceException(ResultCode.NOT_FOUND);
		}

		if (withdrawRec.getType().equals(WithdrawType.POINT_WITHDRAW)) {
			BigDecimal feeRate = getFeeRate(withdrawRec.getPrice());
			//国信手续费
			BigDecimal fee = withdrawRec.getPrice().multiply(feeRate);
			withdrawRec.setPlatformFee(fee);
			//实际提现金额
			BigDecimal actualPrice = withdrawRec.getPrice().subtract(fee);
			withdrawRec.setActualPrice(actualPrice);
		}
		withdrawRec.setWithdrawStatus(WithdrawStatus.WAITING.getValue());
		withdrawRecService.saveOrUpdate(withdrawRec);

		return withdrawRec.getId();
	}

	@Override
	public Long userPointWithdraw(WithdrawRec withdrawRec) {
		//1.保存提现记录
		Long withdrawId = userBalanceWithdraw(withdrawRec);

		//2.转账金额 = 提现金额 - 提现金额 * 国信手续费率
		BigDecimal fee = withdrawRec.getPrice().multiply(getFeeRate(withdrawRec.getPrice()));
		BigDecimal actualPrice = withdrawRec.getPrice().subtract(fee);

		//3.保存转账记录
		TransferRec transferRec = new TransferRec();
		transferRec.setChannel(withdrawRec.getChannel());
		transferRec.setTransferStatus(TransferStatus.REQUEST_RECEIVE);
		transferRec.setOwnerId(withdrawRec.getOwnerId());
		transferRec.setPrice(actualPrice);
		transferRec.setActualPrice(actualPrice);
		transferRec.setFeeChargeSide(YeepayApiConstant.feeChargeSide.INSIDE);
		//转账平台暂时不收取手续费
		transferRec.setPlatformFee(BigDecimal.ZERO);
		//提现记录id
		transferRec.setWithdrawId(withdrawId);
		transferRec.setTenantId(withdrawRec.getTenantId());
		transferRecService.saveOrUpdate(transferRec);

		return transferRec.getId();
	}

    @Override
	@Transactional
    public Boolean submitNotice(NoticeManagementContent noticeContent) {
		Long id = noticeContent.getId();
		//保存通知内容
		noticeContent.setSenderType(SenderType.SHOP);
		boolean flag = noticeContentService.saveOrUpdate(noticeContent);
		if (!Objects.isNull(id)) {
			//如果是修改内容，noticeManagement表不用修改
			return flag;
		}
		List<Member> members = memberService.list(Condition.getQueryWrapper(new Member()).lambda()
			.eq(Member::getShopId, noticeContent.getSenderId()));
		List<NoticeManagement> notices = new ArrayList<>();

		members.forEach(member -> {
			NoticeManagement noticeNew = new NoticeManagement();
			noticeNew.setContentId(noticeContent.getId());
			noticeNew.setReceiverType(ReceiverType.CONSUMER);
			noticeNew.setReceiver(member.getNickName());
			noticeNew.setReceiverId(member.getUserId());
			noticeNew.setIsRead(false);
			notices.add(noticeNew);
		});
		return noticeManagementService.saveBatch(notices);
    }

    /**
	 * 提现处理国信手续费
	 *
	 * @param amount 提现金额
	 * @return 手续费率
	 */
	private BigDecimal getFeeRate(BigDecimal amount) {

		PlatformConfig platformConfig = PlatformCache.platFormConfig();
		Assert.notNull(platformConfig, "提现异常，请联系管理员！");
		List<PlatformConfig.WithdrawConfig> items = platformConfig.getWithdrawConfigList();
		//手续费率
		BigDecimal feeRate = BigDecimal.ZERO;
		for (PlatformConfig.WithdrawConfig item : items) {
			BigDecimal min = BigDecimal.valueOf(item.getMinBalance());
			BigDecimal max = BigDecimal.valueOf(item.getMaxBalance());
			//最小提现金额 <= 提现金额 <= 最大提现金额
			boolean flag = amount.compareTo(min) >= 0 && amount.compareTo(max) <= 0;
			if (flag) {
				feeRate = BigDecimal.valueOf(item.getRate());
			}
		}
		return feeRate;
	}

	@Override
	public BalanceAccountInfoVO queryBalance(Long shopId) {
		Shop shop = this.getById(shopId);
		SettleBalanceQueryDto settleBalanceQueryDto = new SettleBalanceQueryDto(YeepayApiConstant.operatePeriod.ALL.name(), null);
		settleBalanceQueryDto.setMerchantNo(shop.getMerchantNo());
		settleBalanceQueryDto.setParentMerchantNo(yeePayConst.getPlatformServiceNo());
		JSONObject settleBalance = JSON.parseObject(yeepaySaasService.settleBalanceQuery(settleBalanceQueryDto).getStringResult());

		BigDecimal availd = BigDecimal.valueOf(settleBalance.getDouble("settlableAmount"));
		BigDecimal settle = BigDecimal.valueOf(settleBalance.getDouble("unsettledAmount"));
		BigDecimal today = settle.subtract(availd);

		BalanceAccountInfoVO balanceAccountInfoVO = BalanceAccountInfoVO.builder()
			.totalNum(shop.getTotalBalance().toString())
			.availableNum(availd.toString())
			.todayNum(today.toString())
			.build();
		return balanceAccountInfoVO;
	}

	private void handelPromote(Shop shop) {
		Account account = accountService.getOne(Condition.getQueryWrapper(new Account()).lambda()
			.eq(Account::getType, AppConstant.PLATFORM.SHOP.name())
			.eq(Account::getPhone, shop.getPersonTel()));
		if (Objects.isNull(account)) {
			return;
		}
		if (!Objects.isNull(account.getAgentId())) {
			//获取代理信息
			shop.setAgenter(account.getAgentId());
			LoginUser loginUser = loginUserService.getById(account.getAgentId());
			String avatar = Optional.ofNullable(loginUser).map(LoginUser::getAvatar).orElse("");
			shop.setAgentAvatar(avatar);
			String nickName = Optional.ofNullable(loginUser).map(LoginUser::getNickName).orElse("");
			shop.setAgentName(nickName);
		}

		if (!Objects.isNull(account.getSalesmanId())) {
			shop.setSalesmanId(account.getSalesmanId());
		}
	}
}
