package com.qunzhixinxi.hnqz.admin.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import com.qunzhixinxi.hnqz.admin.api.entity.SysDictItem;
import com.qunzhixinxi.hnqz.admin.entity.WmScorePackage;
import com.qunzhixinxi.hnqz.admin.entity.WmScorePackageSettleNote;
import com.qunzhixinxi.hnqz.admin.entity.input.WmScorePackageSettleInput;
import com.qunzhixinxi.hnqz.admin.entity.input.WmScorePackageSettleOutput;
import com.qunzhixinxi.hnqz.admin.entity.model.excel.WmScorePackageSettleExcelModel;
import com.qunzhixinxi.hnqz.admin.enums.PackageFinishStatusEnum;
import com.qunzhixinxi.hnqz.admin.enums.PackageTypeEnum;
import com.qunzhixinxi.hnqz.admin.enums.SettleStatusEnum;
import com.qunzhixinxi.hnqz.admin.enums.SubjectTypeEnum;
import com.qunzhixinxi.hnqz.admin.service.SysDictItemService;
import com.qunzhixinxi.hnqz.admin.service.WmPayOffService;
import com.qunzhixinxi.hnqz.admin.service.WmScorePackageService;
import com.qunzhixinxi.hnqz.common.core.util.R;
import com.qunzhixinxi.hnqz.common.ding.enums.DingEnum;
import com.qunzhixinxi.hnqz.common.log.annotation.SysLog;
import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
import com.qunzhixinxi.hnqz.common.security.util.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 结算表
 *
 * @author Liujh
 * @date 2020-12-1
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/settle")
@Api(value = "settle", tags = "结算管理")
public class WmPayOffController {

	private final WmPayOffService wmPayOffService;
	private final WmScorePackageService wmScorePackageService;
	private final RedisTemplate<String, Object> redisTemplate;
	private final SysDictItemService dictItemService;

	/**
	 * 财务回退操作
	 *
	 * @param input 操作参数
	 * @return 回退结果
	 */
	@ApiOperation(value = "结算回退", notes = "提交结算")
	@SysLog("提交结算")
	@PostMapping("/settleBack")
	public R<?> settleBack(@RequestBody WmScorePackageSettleInput input) {
		if (StringUtils.isEmpty(input.getId())) {
			return R.failed("数据异常");
		}
		return wmPayOffService.settleBack(input);
	}


	/**
	 * 获取批量结算token，三分钟有效
	 * token格式：[租户i]d:order_batch:[用户id]:token:
	 *
	 * @return 结算token
	 */
	@GetMapping(value = "/token")
	public String getOrderBatchToken() {

		String randomStr;

		// 获取操作员
		HnqzUser finaAdmin = SecurityUtils.getUser();
		final String token = String.format("%d:order_batch:%d:token", finaAdmin.getTenantId(), finaAdmin.getId());

		// 返回缓存生成好的
		if (Boolean.TRUE.equals(redisTemplate.hasKey(token))) {
			return (String) redisTemplate.opsForValue().get(token);
		}

		// 生成缓存key,将token缓存到redis
		do {
			randomStr = RandomStringUtils.randomNumeric(6);
		} while (Boolean.FALSE.equals(redisTemplate.opsForValue().setIfAbsent(token, randomStr, 3, TimeUnit.MINUTES)));

		return randomStr;

	}

	/**
	 * 业务提交结算信息到财务
	 *
	 * @param input 结算信息
	 * @return 提交结果
	 */
	@ApiOperation(value = "提交结算", notes = "提交结算")
	@SysLog("提交结算")
	@PostMapping("/settleSubmit")
	public R<?> settleSubmit(@RequestBody @Valid WmScorePackageSettleInput input) {

		//1、验证令牌是否合法【令牌的对比和删除必须保证原子性】
		HnqzUser finaAdmin = SecurityUtils.getUser();
		final String REDIS_DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		Long execute = redisTemplate.execute(new DefaultRedisScript<>(REDIS_DEL_SCRIPT, Long.class),
				Collections.singletonList(String.format("%d:order_batch:%d:token", finaAdmin.getTenantId(), finaAdmin.getId())), input.getToken());
		// 通过EVAL脚本原子验证令牌和删除令牌
		boolean oops = (execute != null && execute == 0);

		// 令牌验证失败
		if (oops) {
			log.error(String.format("提交 TOKEN 不正确：%s", input.getToken()));
			return R.failed("提交 TOKEN 不正确，请刷新页面重试");
		}

		if (input.getSubjectLocation() == null) {
			return R.failed("人员结算渠道不存在，不能结算");
		}

		return wmPayOffService.settleSubmit(input);
	}

	/**
	 * 业务保存结算记录
	 *
	 * @param input 结算信息
	 * @return 提交结果
	 */
	@ApiOperation(value = "保存结算", notes = "保存结算")
	@SysLog("保存结算")
	@PostMapping("/settleSave")
	public R<?> settleSave(@RequestBody WmScorePackageSettleInput input) {

		if (CollectionUtils.isEmpty(input.getNotes()) || StringUtils.isEmpty(input.getId()) || null == input.getInvoiceCategory() || input.getSubjectLocation() == null) {

			return R.failed("数据异常或人员结算渠道不存在，不能结算");
		}

		return wmPayOffService.settleSave(input);
	}


	/**
	 * 重新结算
	 *
	 * @param input 结算信息
	 * @return 提交结果
	 */
	@ApiOperation(value = "重新结算", notes = "重新结算")
	@SysLog("重新结算")
	@PostMapping("/settleReIssue")
	public R<?> settleReIssue(@RequestBody WmScorePackageSettleInput input) {
		if (StringUtils.isEmpty(input.getId())) {
			return R.failed("数据异常");
		}

		return wmPayOffService.settleReIssueBySubType(input);
	}

	/**
	 * 业务展示结算信息
	 *
	 * @param input 积分包id
	 * @return 待提交的信息
	 */
	@ApiOperation(value = "结算", notes = "结算")
	@SysLog("结算")
	@PostMapping("/settleById")
	public R<?> settleById(@RequestBody WmScorePackageSettleInput input) {

		if (input == null || StrUtil.isBlank(input.getId())) {
			return R.failed("积分包id缺失");
		}

		return wmPayOffService.settleById(input.getId());
	}


	/**
	 * 财务审核页面
	 *
	 * @param page  分页信息
	 * @param input 参数
	 * @return 分页结果
	 */
	@ApiOperation(value = "分页查询结算", notes = "分页查询结算")
	@SysLog("分页查询结算")
	@GetMapping("/listPackageSettleList")
	public R<?> listPackageSettleList(Page<WmScorePackageSettleOutput> page, WmScorePackageSettleInput input) {

		input.setSendPackageDeptId(String.valueOf(SecurityUtils.getUser().getDeptId()));
		input.setPackageFinishStatus("1");
		input.setTypeid("4");
		input.setSettleFlag("1");
		return R.ok(wmScorePackageService.listPackageSettleList(page, input));
	}

	/**
	 * 结算包结算导出
	 *
	 * @param input 输入参数
	 * @return excel模型
	 */
	@ApiOperation(value = "积分包结算导出", notes = "积分包结算导出")
	@ResponseExcel(name = "exportPackageSettle", sheet = "积分包结算数据")
	@SysLog("积分包结算导出")
	@GetMapping("/exportPackageSettle")
	public List<WmScorePackageSettleExcelModel> exportPackageSettle(WmScorePackageSettleInput input) {
		input.setSendPackageDeptId(String.valueOf(SecurityUtils.getUser().getDeptId()));
		input.setPackageFinishStatus("1");
		input.setTypeid("4");
		input.setSettleFlag("1");
		List<WmScorePackageSettleOutput> list = wmScorePackageService.listAllPackageSettle(input);
		List<WmScorePackageSettleExcelModel> exportList = new ArrayList<>();
		if (CollectionUtil.isEmpty(list)) {
			return Collections.emptyList();
		}

		// 获取发票类目信息
		List<SysDictItem> invoiceCategoryList = dictItemService.list(Wrappers.<SysDictItem>lambdaQuery().in(SysDictItem::getType, "renlijia_invoice", "olading_invoice", "yee_invoice", "tax_helper_invoice"));
		Map<String, Map<String, String>> collect = invoiceCategoryList.stream().collect(Collectors.groupingBy(SysDictItem::getType, Collectors.toMap(SysDictItem::getValue, SysDictItem::getLabel)));
		Map<String, String> renlijiaInvoice = collect.get("renlijia_invoice");
		Map<String, String> oladingInvoice = collect.get("olading_invoice");
		Map<String, String> yeeInvoice = collect.get("yee_invoice");
		Map<String, String> taxHelperInvoice = collect.get("tax_helper_invoice");


		// 转换为导出实体
		for (int i = 0; i < list.size(); i++) {
			WmScorePackageSettleOutput settleOutput = list.get(i);
			WmScorePackageSettleExcelModel export = new WmScorePackageSettleExcelModel();
			export.setNumber(i + 1);
			export.setScorePackageName(settleOutput.getScorePackageName());
			export.setPackageType1(PackageTypeEnum.type1Resolve(settleOutput.getPackageType1()).getName2());
			export.setPackageEntName(settleOutput.getPackageEntName());
			export.setCreateTime(settleOutput.getCreateTime());
			export.setPackageFinishStatus(PackageFinishStatusEnum.resolve(settleOutput.getPackageFinishStatus()).getName());
			export.setScore(settleOutput.getScore());

			SubjectTypeEnum subjectTypeEnum = SubjectTypeEnum.resolve(settleOutput.getSubType());
			export.setSubType(subjectTypeEnum.getDesc());
			String cate = String.valueOf(settleOutput.getInvoiceCategory());
			String invoice;
			switch (subjectTypeEnum) {
				case TYPE_RENLIJIA:
					invoice = renlijiaInvoice.get(cate);
					break;
				case TYPE_SHUIBANGYUN:
					invoice = taxHelperInvoice.get(cate);
					break;
				case TYPE_OLADING:
					invoice = oladingInvoice.get(cate);
					break;
				case TYPE_YEE:
					invoice = yeeInvoice.get(cate);
					break;
				case TYPE_NULL:
				default:
					invoice = WmScorePackageSettleExcelModel.CategoryEnum.CATEGORY_NULL.getDesc();
					break;
			}
			export.setInvoiceCategory(invoice);
			export.setUserNames(settleOutput.getUserNames());
			export.setSettleAmount(settleOutput.getSettleAmount());
			export.setSettleStatus(SettleStatusEnum.resolve(settleOutput.getSettleStatus()).getName());
			export.setBelongDate(settleOutput.getBelongDate());
			exportList.add(export);
		}

		return exportList;
	}

	@ApiOperation(value = "结算", notes = "结算")
	@SysLog("结算")
	@PostMapping("/saveSettleInfo")
	public R<?> saveSettleInfo(@RequestBody List<WmScorePackage> wsList) {

		if (CollectionUtils.isEmpty(wsList)) {
			return R.failed("请选择数据");
		}

		Optional<WmScorePackage> any = wsList.stream().filter(ws -> StringUtils.isEmpty(ws.getId())).findAny();

		if (any.isPresent()) {
			return R.failed("数据异常，请联系管理员：" + any.get().getScoreName());
		}

		return wmPayOffService.saveSettleInfo(wsList);
	}

	/**
	 * 结算
	 *
	 * @param input 结算信息
	 * @return 结算操作结果
	 */
	@ApiOperation(value = "结算-自由选择渠道", notes = "结算-自由选择渠道")
	@SysLog("结算-自由选择渠道")
	@PostMapping("/settleIssueBySubType")
	public R<?> settleIssueBySubType(@RequestBody WmScorePackageSettleInput input) {

		if (StrUtil.isBlank(input.getId()) || StrUtil.isBlank(input.getScorePackageName()) || input.getSubjectLocation() == null || CollUtil.isEmpty(input.getNotes()) || null == input.getInvoiceCategory()) {
			return R.failed("数据异常");
		}

		return wmPayOffService.settleIssueBySubType(input);
	}

	/**
	 * 批量财务结算
	 *
	 * @param inputList 批量结算
	 * @return 结算操作结果
	 */
	@SysLog("批量财务结算")
	@PostMapping("/batch-financial-settle")
	@SuppressWarnings("all")
	public R<?> batchFinancialSettle(@RequestBody List<WmScorePackageSettleInput> inputList) {

		if (CollectionUtil.isEmpty(inputList)) {
			return R.failed("数据异常");
		}

		List<Map<String, String>> msgList = new ArrayList<>();

		for (WmScorePackageSettleInput input : inputList) {

			if (StringUtils.isEmpty(input.getId()) || null == input.getSubjectLocation() || null == input.getInvoiceCategory() || StringUtils.isEmpty(input.getScorePackageName())) {
				return R.failed("数据异常");
			}

			// 查询结算详情（用于拼接notes数据）
			R<?> queryR = wmPayOffService.settleById(input.getId());

			if (queryR.getCode() == 0) {
				Map<String, Object> queryData = (Map<String, Object>) queryR.getData();
				List<WmScorePackageSettleNote> notes = (List<WmScorePackageSettleNote>) queryData.get("notes");
				input.setNotes(notes);
				// 调用单个结算
				R<?> settleR = wmPayOffService.settleIssueBySubType(input);
				boolean failFlag;
				if (settleR.getCode() == 0) {
					// 结算成功，不进行页面提示
					Optional<WmScorePackageSettleNote> any = notes.stream().filter(note -> !DingEnum.NOTE_STATUS_SUBMIT.getType().equals(note.getSettleNoteStatus())).findAny();
					failFlag = any.isPresent();
				} else {
					failFlag = true;
				}

				if (failFlag) {
					Map<String, String> msgMap = new HashMap<>(2);
					msgMap.put("packageName", input.getScorePackageName());
					msgMap.put("reason", settleR.getMsg());
					msgList.add(msgMap);
				}

			} else {
				Map<String, String> msgMap = new HashMap<>(2);
				msgMap.put("packageName", input.getScorePackageName());
				msgMap.put("reason", queryR.getMsg());
				msgList.add(msgMap);
			}
		}

		return R.ok(msgList);
	}

}
