package com.palmnest.webapp.controller.wechat;

import com.alibaba.fastjson.JSONObject;
import com.palmnest.application.WxPayConstant;
import com.palmnest.application.bean.AppResultDto;
import com.palmnest.application.core.util.AppResultUtil;
import com.palmnest.application.core.util.HttpKit;
import com.palmnest.application.core.util.PropertiesKit;
import com.palmnest.application.core.util.wxpay.PayUtils;
import com.palmnest.application.dto.output.wechat.MerchantDetailDto;
import com.palmnest.model.wxpay.AuthToken;
import com.palmnest.model.wxpay.PaySendData;
import com.palmnest.service.wechat.PayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

@Slf4j
@Controller
@RequestMapping(value = "/wechat/pay")
public class WeChatPayController {


	@Autowired
	private PayService payService;


	/**
	 * 获取微信号用户授权URL
	 * 参数：
	 *
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/authUrl")
	@ResponseBody
	public AppResultDto index(HttpServletRequest request) {

		String orgId      = request.getParameter("orgId");
		String source     = request.getParameter("source");
		String moduleCode = request.getParameter("moduleCode");

		//TODO source限制只有特定来源才能调取接口
		//根据项目id和模块信息获取对应商户信息
		if (StringUtils.isBlank(orgId) && StringUtils.isBlank(moduleCode)) {
			return AppResultUtil.createErrorDto(WxPayConstant.GET_AUTH_URL_ERROR, "传入的项目ID和模块编码为空");
		}

		Map<String, String> params = new HashMap<String, String>();

		if (StringUtils.isNotBlank(orgId)) {
			params.put("orgId", orgId);
		}

		if (StringUtils.isNotBlank(moduleCode)) {
			params.put("moduleCode", moduleCode);
		}

		List<MerchantDetailDto> dtos  = payService.findBy(params);
		String                  appId = WxPayConstant.APP_ID;
		if (dtos.size() >= 1) {
			MerchantDetailDto dto = dtos.get(0);
			appId = dto.getAppId();
		} else {
			return AppResultUtil.createErrorDto(WxPayConstant.GET_AUTH_URL_ERROR, "没有查询到对应商户信息");
		}
		try {
			String redirectUri = URLEncoder.encode(request.getParameter("redirect_uri"), "utf-8");//编码
			String authUrl     = WxPayConstant.PAY_URL(appId, redirectUri);
			return AppResultUtil.createSuccesDto(authUrl);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			log.error("获取授权URL失败：" + e);
		}
		return AppResultUtil.createErrorDto(WxPayConstant.GET_AUTH_URL_ERROR, "获取授权URL失败");
	}


	private String getPrepayId(HttpServletRequest request, String appId, String appSecret, String mchId) {
		String openId = request.getParameter("openId");
//		String notifyUrl = WxPayConstant.NOTIFY_URL;
		String notifyUrl  = request.getParameter("notify_url"); //异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数  必填
		String outTradeNo = request.getParameter("out_trade_no");//商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|* 且在同一个商户号下唯一。 必填
		String body       = request.getParameter("body"); //商品简单描述，该字段请按照规范传递 必填
		String detail     = request.getParameter("detail"); //商品详细描述，对于使用单品优惠的商户，改字段必须按照规范上传
		String attach     = request.getParameter("attach"); //附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
		String totalFee   = request.getParameter("total_fee"); //单位为分  必填
		if (StringUtils.isBlank(openId)) {
			String    code      = request.getParameter("code");
			AuthToken authToken = PayUtils.getTokenByAuthCode(code, appId, appSecret);
			openId = authToken.getOpenid();
		}

		PaySendData paySendData = new PaySendData();
		// 构建微信支付请求参数集合
		paySendData.setAppId(appId);
		paySendData.setMch_id(mchId);
		paySendData.setNotify_url(notifyUrl);
		paySendData.setTrade_type(WxPayConstant.TRADE_TYPE_JSAPI);
		paySendData.setDevice_info(WxPayConstant.WEB);
		paySendData.setBody(body);
		paySendData.setDetail(detail);
		paySendData.setAttach(attach);
		paySendData.setNonce_str(PayUtils.getRandomStr(32));//必填
		paySendData.setOut_trade_no(outTradeNo);
		if (PayUtils.isEmpty(outTradeNo)) {
			//如果没有传，自动生成
			paySendData.setOut_trade_no(PayUtils.getRandomStr(20));
		}
		paySendData.setTotal_fee(Integer.parseInt(totalFee));
		paySendData.setSpbill_create_ip(request.getRemoteAddr());
		paySendData.setOpenId(openId);
		// 将参数拼成map,生产签名
		paySendData.setSign(PayUtils.getSign(PayUtils.buildParamMap(paySendData)));
		// 调用统一下单service
		String prepayId = payService.unifiedOrder(paySendData);//微信生成的预支付会话标识，用于后续接口调用中使用，该值有效期为2小时
		return prepayId;
	}

	/**
	 * 统一下单
	 */
	@RequestMapping(value = "/unifiedOrder")
	@ResponseBody
	public AppResultDto unifiedOrder(HttpServletRequest request, Model model) {
		// 通过code获取网页授权access_token
		try {
			String orgId      = request.getParameter("orgId");
			String source     = request.getParameter("source");
			String moduleCode = request.getParameter("moduleCode");

			if (StringUtils.isBlank(orgId) && StringUtils.isBlank(moduleCode)) {
				return AppResultUtil.createErrorDto(WxPayConstant.GET_AUTH_URL_ERROR, "传入的项目ID和模块编码为空");
			}
			Map<String, String> params = new HashMap<String, String>();
			if (StringUtils.isNotBlank(orgId)) {
				params.put("orgId", orgId);
			}
			if (StringUtils.isNotBlank(moduleCode)) {
				params.put("moduleCode", moduleCode);
			}
			List<MerchantDetailDto> dtos      = payService.findBy(params);
			String                  appId     = WxPayConstant.APP_ID;//微信支付分配的公众账号ID（企业号corpid即为此appId） 必填
			String                  appSecret = WxPayConstant.APP_SECRET;
			String                  mchId     = WxPayConstant.MCH_ID; //微信支付分配的商户号 必填
			if (dtos.size() >= 1) {
				MerchantDetailDto dto = dtos.get(0);
				appId = dto.getAppId();
				appSecret = dto.getAppSecret();
				mchId = dto.getMchId();
			} else {
				return AppResultUtil.createErrorDto(WxPayConstant.GET_AUTH_URL_ERROR, "没有查询到对应商户信息");
			}
			String prepayId = this.getPrepayId(request, appId, appSecret, mchId);
			if (!PayUtils.isEmpty(prepayId)) {
				String timeStamp = PayUtils.getTimeStamp();// 当前时间戳
				String nonceStr  = PayUtils.getRandomStr(20);// 不长于32位的随机字符串

				SortedMap<String, Object> signMap = new TreeMap<String, Object>();// 自然升序map
				signMap.put("appId", appId);
				signMap.put("package", prepayId);
				signMap.put("timeStamp", timeStamp);
				signMap.put("nonceStr", nonceStr);
				signMap.put("signType", "MD5");

				Map<String, Object> returnMap = new HashMap<String, Object>();
				returnMap.put("appId", appId);
				returnMap.put("timeStamp", timeStamp);
				returnMap.put("nonceStr", nonceStr);
				returnMap.put("prepayId", prepayId);
				returnMap.put("paySign", PayUtils.getSign(signMap));// 获取签名

				return AppResultUtil.createSuccesDto(returnMap);
			} else {
				System.out.println("微信统一下单失败,获取预支付会话标识失败");
				log.error("微信统一下单失败,获取预支付会话标识失败");
				return AppResultUtil.createErrorDto(WxPayConstant.GET_PREPAYID_ERROR, "获取预支付会话标识失败");// 获取预支付会话标识失败
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("微信统一下单失败," + e);
			return AppResultUtil.createErrorDto(WxPayConstant.UNIFIED_ORDER_ERROR, "获取统一支付信息失败");// 支付下单失败
		}
		// 将支付需要参数返回至页面，采用h5方式调用支付接口
	}

	/**
	 * 申请退款
	 */
	@RequestMapping(value = "/refund")
	public String refund(HttpServletRequest request, Model model) {
		// 调用统一下单service
		payService.refund();
		return "wechat/pay/h5Pay";
	}


	// ============================= 统一支付1.0 start =============================
	Map<String, String> openidMap = new HashMap<>();
	private static String wxPayUrl     = PropertiesKit.readPropertiesValue("wxPayUrl", "wechat.properties");
	private static String wxPayTestOrg = PropertiesKit.readPropertiesValue("wxPayTestOrg", "wechat.properties");

	/**
	 * 去支付测试页面
	 */
	@RequestMapping(value = "/payIndex")
	public ModelAndView payIndex(HttpServletRequest request, Model model) {

		String orderNo = request.getParameter("orderNo");
		if (StringUtils.isBlank(orderNo)) {
			orderNo = UUID.randomUUID().toString().replace("-", "");
		}
		model.addAttribute("orderNo", orderNo);
		model.addAttribute("payURL", "http://wx.palmnest.com/super_service/wechat/pay/unifiedOrderTest");
		return new ModelAndView("wechat/pay/index", model.asMap());
	}

	/**
	 * 获取授权连接
	 */
	@RequestMapping(value = "/authUrlTest")
	@ResponseBody
	public AppResultDto authUrlTest(HttpServletRequest request, Model model) {

		String        orderNo = request.getParameter("orderNo");
		StringBuilder param   = new StringBuilder();
		param.append("orgId=").append(wxPayTestOrg)
				.append("&orderNo=").append(orderNo)
//				.append("&callBackUrl=").append("https://wx.palmnest.com/super_service/wechat/pay/fetchOpenid");
				.append("&callBackUrl=").append("https://wx.palmnest.com/super_service/authSkip1");
		String result = HttpKit.Post(param.toString(), wxPayUrl + "/api/wechat/front/pay/authUrl");
		log.info("===== authUrlTest result:" + result);
		JSONObject jsonObject = JSONObject.parseObject(result);
		return AppResultUtil.createSuccesDto(jsonObject.getString("data"));
	}

	/**
	 * 获取openid
	 */
	@RequestMapping(value = "/fetchOpenid1")
	@ResponseBody
	public AppResultDto fetchOpenid1(HttpServletRequest request, Model model) {

		String        orderNo = request.getParameter("orderNo");
		String        code    = request.getParameter("code");
		StringBuilder param   = new StringBuilder();
		param.append("orgId=").append(wxPayTestOrg)
				.append("&orderNo=").append(orderNo)
				.append("&code=").append(code);
		log.info("===== fetchOpenid1 param:" + param.toString());
		String result = HttpKit.Post(param.toString(), wxPayUrl + "/api/wechat/front/pay/authorize2");
		log.info("===== authUrlTest result:" + result);
		JSONObject jsonObject = JSONObject.parseObject(result);

		JSONObject data   = jsonObject.getJSONObject("data");
		String     openId = data.getString("openId");
		if (StringUtils.isNotBlank(openId) && StringUtils.isNotBlank(orderNo))
			openidMap.put(orderNo, openId);

		return AppResultUtil.createSuccesDto();
	}

	/**
	 * 接收用户openid
	 */
	@RequestMapping(value = "/fetchOpenid")
	@ResponseBody
	public void fetchOpenid(String openid, String orderNo) throws Exception {
		log.info("回调获取openid fetchOpenid ======== openid:" + openid + ";orderNo:" + orderNo);
		if (StringUtils.isNotBlank(openid) && StringUtils.isNotBlank(orderNo))
			openidMap.put(orderNo, openid);
	}

	/**
	 * 统一下单
	 */
	@RequestMapping(value = "/unifiedOrderTest")
	public ModelAndView unifiedOrderTest(String orderNo, Model model) throws Exception {

		log.info("统一下单接口 unifiedOrderTest ======== orderNo:" + orderNo);
		// 根据orderNo查询订单信息
		String openid = openidMap.get(orderNo);
		if (StringUtils.isBlank(openid)) {
			log.info("======= openid为空");
			return new ModelAndView("wechat/pay/index");
		}
//		Integer orgId = wxPayTestOrg;
//		String orderNo;
		String orderFee  = "1";
		String orderDesc = "测试-支付";
		String notifyUrl = "https://wx.palmnest.com/super_service/wechat/pay/notifyState";

		StringBuilder param = new StringBuilder();
		param.append("openid=").append(openid)
				.append("&orgId=").append(wxPayTestOrg)
				.append("&orderNo=").append(orderNo)
				.append("&orderFee=").append(orderFee)
				.append("&orderDesc=").append(orderDesc)
				.append("&notifyUrl=").append(notifyUrl)
				.append("&orderSource=").append("FLSC")
				.append("&orderModule=").append("tshh");

		String     url        = wxPayUrl + "/api/wechat/front/pay/unifiedPay";
		String     result     = HttpKit.Post(param.toString(), url);
		JSONObject jsonObject = JSONObject.parseObject(result);
		log.info("======= 调用支付返回结果:" + result);
		String code = jsonObject.getString("code");
		if ("200".equalsIgnoreCase(code)) {
			String redirectUrl = jsonObject.getString("data");
			model.addAttribute("redirectUrl", redirectUrl);
		}

		return new ModelAndView("wechat/pay/h5Pay", model.asMap());
	}

	/**
	 * 发起退款
	 */
	@RequestMapping(value = "/refundOrder")
	@ResponseBody
	public AppResultDto refundOrder(String orderNo) throws Exception {

		log.info("============== notify orderNo:{" + orderNo + "}=================");
		String url    = wxPayUrl + "/api/wechat/front/pay/refund?orderNo=" + orderNo;
		String result = HttpKit.get(url);
		return AppResultUtil.createErrorDto("200", result);
	}

	/**
	 * 订单支付状态回调
	 */
	@RequestMapping(value = "/notifyState")
	@ResponseBody
	public AppResultDto notifyState(String orderNo, String state, String type) throws Exception {

		log.info("============== notify orderNo:{" + orderNo + "},state:{" + state + "},type:{" + type + "} =================");
		return AppResultUtil.createErrorDto("", "");
	}
	// ============================= 统一支付1.0 end =============================


	// ============================= 统一支付2.0 start =============================

	/**
	 * 统一支付2.0测试
	 */
	@RequestMapping(value = "/unifiedPay2")
	public ModelAndView unifiedPay2(Model model) throws Exception {

		// 项目id
		Integer orgId = 746;
		// 订单号,唯一字符串
		String orderNo = UUID.randomUUID().toString().replace("-", "");
		// 订单金额,单位分
		String orderFee = "1";
		// 订单描述
		String orderDesc = "测试-支付";
		// 订单支付状态回调通知
		String notifyUrl = "https://wx.palmnest.com/super_service/wechat/pay/notifyState";
		// 订单支付成功后跳转页面地址
		String resultPageUrl = "https://wx.palmnest.com/super_service/result";

		StringBuilder param = new StringBuilder();
		param.append("&orgId=").append(orgId)
				.append("&orderNo=").append(orderNo)
				.append("&orderFee=").append(orderFee)
				.append("&orderDesc=").append(orderDesc)
				.append("&notifyUrl=").append(notifyUrl)
				.append("&orderSource=").append("FLSC")
				.append("&orderModule=").append("tshh")
				.append("&resultPageUrl=").append(resultPageUrl);

		String     url        = "http://wx.palmnest.com/super_cloud/api/wechat/front/pay/v2/unifiedPay";
		String     result     = HttpKit.Post(param.toString(), url);
		JSONObject jsonObject = JSONObject.parseObject(result);
		String     resultUrl  = jsonObject.getString("data");
		model.addAttribute("resultUrl", resultUrl);
		return new ModelAndView("wechat/pay/result2", model.asMap());
	}
	// ============================= 统一支付2.0 end =============================
}