package com.iformall.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.iformall.annotation.SystemControllerLog;
import com.iformall.common.ErrorCode;
import com.iformall.domain.po.*;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.pay.WxPayConstant;
import com.iformall.pay.WxPayment;
import com.iformall.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.text.SimpleDateFormat;
import java.util.*;

@RestController
@AllArgsConstructor
@Api(description = "POS相关接口")
public class PosController extends BaseController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final SimpleDateFormat neuDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");

    private final PosService posService;
    private final ThDevInfoService thDevInfoService;
    private final WxCouponOrderService couponOrderService;

    @ApiOperation(value = "门店用户登录检查, 同时会返回会员折扣/优惠券/消费卡是否启用，mall信息及注册二维码及小票二维码规则", notes = "request: \n{" +
            "\"dev_id\":\"string(必填)\"," +
            "\"nonce_str\":\"string(必填)," +
            "\"tenant_id\":\"string(必填)\"," +
            "\"phone\":\"string(必填)\"," +
            "\"password\":\"string(必填)\"}\n" +
            "response: \n{" +
            "\"return_code\":\"SUCCESS\",\"return_msg\":\"\",\"result_code\":\"SUCCESS\"," +
            "\"tenant_id\":\"456\",\"mall_name\":\"富茂中心\"," +
            "\"merchant_id\":\"320833942159982592\",\"merchant_name\":\"东软合作\"," +
            "\"merchant_img\":\"https://s3.cn-northwest-1.amazonaws.com.cn/iformall-net/234/WechatIMG5071.png\"," +
            "\"order_operation_id\":\"320834180694245376\"," +
            "\"qrcode_url\":\"https://s3.cn-northwest-1.amazonaws.com.cn/iformall-net/456/formall_c.png\"," +
            "\"pos_qrcode_rule\":\"https://ctest.malls.iformall.com/order/(二维码规则: url + POS订单ID)\"," +
            "\"discount\":\"false(是否支持会员折扣,false:禁用,true:可用)\"," +
            "\"coupon\":\"0(会员优惠券是否开启,0-禁用,1-单选可用,2-多选可用)\",\"coupon_desc\":\"禁用(会员优惠券是否开启描述)\"," +
            "\"card\":\"false(是否支持卡支付,false:禁用,true:可用)\"" +
            "\"nonce_str\":\"2d8320f95e73419ebd42fcfded970d5b\"," +
            "\"sign\":\"62139EE7897CF3BA44D9DBECA95AF6B116A9C49BBBB574BF3D2AF2939EE30423\"}")
    @PostMapping("/checkUserPassword")
    @SystemControllerLog(description = "门店用户登录检查")
    public Map<String, String> checkUserPassword(@RequestBody Map<String, String> params) {
        JSONObject payContent = new JSONObject();
        JSONObject retObj = new JSONObject();

        // 1. check sign
        Map<String, String> retMap = checkSign(params, payContent);
        if (retMap != null) {
            // 签名相关异常返回
            return retMap;
        }
        String resKey = payContent.getString(WxPayConstant.RES_KEY);
        logger.info(WxPayConstant.RES_KEY + ": " + resKey);

        try {
            retObj = posService.checkUserPassword(params);
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", null);
        } catch (MallinkException e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", 500, e.getMessage());
        }
    }

    @ApiOperation(value = "查询及试算接口", notes = "{" +
            "\"dev_id\":\"string(必填)\"," +
            "\"nonce_str\":\"string(必填)\"," +
            "\"tenant_id\":\"string(必填)\"," +
            "\"merchant_id\":\"string(必填)\"," +
            "\"order_operator_id\":\"string(必填)\"," +
            "\"mem_id\":\"string(选填)\"," +
            "\"mem_phone_number\":\"string(选填)\"," +
            "\"card_id\":\"string(选填)\"," +
            "\"business_type\":\"string(0:核销1支付(必填)\"," +
            "\"order_amount\":\"string单位(分)(必填)\"," +
            "\"order_amount_left\":\"string单位(分)(必填)\"," +
            "\"order_create_sn\":\"string单位(分)(必填)\"," +
            "\"coupon_list\":\"json array[券包ID list](必填),例[\"1\",\"2\",\"3\"]\"}")
    @PostMapping("/queryAndCalc")
    @SystemControllerLog(description = "支付计算接口")
    public Map<String, String> calcForMem(@RequestBody Map<String, String> params) {
        JSONObject payContent = new JSONObject();
        JSONObject retObj = new JSONObject();

        // 1. check sign
        Map<String, String> retMap = checkSign(params, payContent);
        if (retMap != null) {
            // 签名相关异常返回
            return retMap;
        }
        String resKey = payContent.getString(WxPayConstant.RES_KEY);
        logger.info(WxPayConstant.RES_KEY + ": " + resKey);

        try {
            retObj = posService.queryAndCalcMem(params);
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",null);
        } catch (MallinkException e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", 500, e.getMessage());
        }
    }

    @ApiOperation(value = "优惠核销接口", notes = "{" +
            "\"dev_id\":\"string(必填)\"," +
            "\"nonce_str\":\"string(必填)\"," +
            "\"tenant_id\":\"string(必填)\"," +
            "\"merchant_id\":\"string(必填)\"," +
            "\"order_operator_id\":\"string(必填)\"," +
            "\"mem_id\":\"string(选填)\"," +
            "\"mem_phone_number\":\"string(选填)\"," +
            "\"business_type\":\"string(0:核销1支付(必填)\"," +
            "\"order_number_of_platform\":\"string(必填)\"," +
            "\"order_amount\":\"string单位(分)(必填)\"," +
            "\"order_amount_left\":\"string单位(分)(必填)\"," +
            "\"order_create_sn\":\"string单位(分)(必填)\"," +
            "\"coupon_list\":\"json array[券包ID list](必填),例[\"1\",\"2\",\"3\"]\"}")
    @PostMapping("/couponOrderVerify")
    @SystemControllerLog(description = "优惠核销接口")
    public Map<String, String> couponOrderVerify(@RequestBody Map<String, String> params) {
        JSONObject payContent = new JSONObject();
        JSONObject retObj = new JSONObject();

        // 1. check sign
        Map<String, String> retMap = checkSign(params, payContent);
        if (retMap != null) {
            // 签名相关异常返回
            return retMap;
        }
        String resKey = payContent.getString(WxPayConstant.RES_KEY);
        logger.info(WxPayConstant.RES_KEY + ": " + resKey);

        try {
            retObj = posService.couponOrderVerify(params);
        } catch (MallinkException e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", 500, e.getMessage());
        }
        return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",null);
    }

    @ApiOperation(value = "券核销取消", notes = "{" +
            "\"dev_id\":\"string(必填)\"," +
            "\"nonce_str\":\"string(必填)\"," +
            "\"tenant_id\":\"string(必填)\"," +
            "\"merchant_id\":\"string(必填)\"," +
            "\"order_operator_id\":\"string(必填)\"," +
            "\"mem_id\":\"string(选填)\"," +
            "\"mem_phone_number\":\"string(选填)\"," +
            "\"business_type\":\"string(0:核销1支付(必填)\"," +
            "\"order_number_of_platform\":\"string(必填)\"," +
            "\"order_amount\":\"string单位(分)(选填)\"," +
            "\"order_amount_left\":\"string单位(分)(选填)\"," +
            "\"order_create_sn\":\"string单位(分)(必填)\"," +
            "\"payment_info\":{" +
            "\"coupon_id\":\"string(必填)\"," +
            "\"order_amount\":\"string单位(分)(选填)\"," +
            "\"amount_left\":\"string单位(分)(选填)\"," +
            "\"promotion_amount\":\"string单位(分)(选填)\"," +
            "\"amount_left_after_pay\":\"string单位(分)(选填)\"" +
            "}}")
    @PostMapping("/couponOrderVerifyCancel")
    @SystemControllerLog(description = "券核销取消")
    public Map<String, String> couponOrderIndependentVerifyCancel(@RequestBody Map<String, String> params) {
        JSONObject payContent = new JSONObject();
        JSONObject retObj = new JSONObject();

        // 1. check sign
        Map<String, String> retMap = checkSign(params, payContent);
        if (retMap != null) {
            // 签名相关异常返回
            return retMap;
        }
        String resKey = payContent.getString(WxPayConstant.RES_KEY);
        logger.info(WxPayConstant.RES_KEY + ": " + resKey);

        try {
            retObj = posService.couponOrderVerifyCancel(params);
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",null);
        } catch (MallinkException e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",500, e.getMessage());
        }
    }

    @ApiOperation(value = "消费卡支付", notes = "{" +
            "\"dev_id\":\"string(必填)\"," +
            "\"nonce_str\":\"string(必填)\"," +
            "\"tenant_id\":\"string(必填)\"," +
            "\"merchant_id\":\"string(必填)\"," +
            "\"order_operation_id\":\"string(必填)\"," +
            "\"business_type\":\"string(0:核销1支付(必填)\"," +
            "\"order_number_of_platform\":\"string(必填)\"," +
            "\"order_amount\":\"string单位(分)(选填)\"," +
            "\"order_amount_left\":\"string单位(分)(选填)\"," +
            "\"order_create_sn\":\"string单位(分)(必填)\"," +
            "\"card_id\":\"string(必填)\"," +
            "\"amount_paid_by_card\":\"string(必填)\"}")
    @PostMapping("/cardPayPre")
    @SystemControllerLog(description = "消费卡支付")
    public Map<String, String> cardPayPre(@RequestBody Map<String, String> params) {
        JSONObject payContent = new JSONObject();
        JSONObject retObj = new JSONObject();

        // 1. check sign
        Map<String, String> retMap = checkSign(params, payContent);
        if (retMap != null) {
            // 签名相关异常返回
            return retMap;
        }
        String resKey = payContent.getString(WxPayConstant.RES_KEY);
        logger.info(WxPayConstant.RES_KEY + ": " + resKey);

        try {
            retObj = posService.cardPayPre(params);
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",null);
        } catch (MallinkException e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", 500, e.getMessage());
        }
    }

    @ApiOperation(value = "消费卡支付取消", notes = "{" +
            "\"dev_id\":\"string(必填)\"," +
            "\"nonce_str\":\"string(必填)\"," +
            "\"tenant_id\":\"string(必填)\"," +
            "\"merchant_id\":\"string(必填)\"," +
            "\"order_operation_id\":\"string(必填)\"," +
            "\"business_type\":\"string(0:核销1支付(必填)\"," +
            "\"order_number_of_platform\":\"string(必填)\"," +
            "\"order_amount\":\"string单位(分)(选填)\"," +
            "\"order_amount_left\":\"string单位(分)(选填)\"," +
            "\"order_create_sn\":\"string单位(分)(必填)\"," +
            "\"payment_info\":{" +
            "\"card_id\":\"string(必填)\"," +
            "\"card_spend_id\":\"string(必填)\"," +
            "\"amount_paid_by_card\":\"string单位(分)(选填)\"," +
            "\"amount_left_after_pay\":\"string单位(分)(选填)\"" +
            "}}")
    @PostMapping("/cardPayPreCancel")
    @SystemControllerLog(description = "消费卡支付取消")
    public Map<String, String> cardPayPreCancel(@RequestBody Map<String, String> params) {
        JSONObject payContent = new JSONObject();
        JSONObject retObj = new JSONObject();

        // 1. check sign
        Map<String, String> retMap = checkSign(params, payContent);
        if (retMap != null) {
            // 签名相关异常返回
            return retMap;
        }
        String resKey = payContent.getString(WxPayConstant.RES_KEY);
        logger.info(WxPayConstant.RES_KEY + ": " + resKey);

        try {
            retObj = posService.cardPayPreCancel(params);
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",null);
        } catch (MallinkException e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",500, e.getMessage());
        }
    }

    @ApiOperation(value = "POS订单同步", notes = "支付成功/支付取消后订单同步" +
            "{" +
            "\"dev_id\":\"string(必填)\"," +
            "\"nonce_str\":\"string(必填)\"," +
            "\"tenant_id\":\"string(必填)\"," +
            "\"merchant_id\":\"string(必填)\"," +
            "\"order_operation_id\":\"string(必填)\"," +
            "\"business_type\":\"string(0:核销1支付(必填)\"," +
            "\"mem_id\":\"string(必填)\"," +
            "\"mem_phone_number\":\"string(必填)\"," +
            "\"order_number_of_platform\":\"string(必填)\"," +
            "\"order_amount\":\"string单位(分)(选填)\"," +
            "\"order_amount_left\":\"string单位(分)(选填)\"," +
            "\"order_create_sn\":\"string单位(分)(必填)\"," +
            "\"payment_info\":{" +
            "\"card_id\":\"string(必填)\"," +
            "\"card_spend_id\":\"string(必填)\"," +
            "\"amount_paid_by_card\":\"string单位(分)(选填)\"," +
            "\"amount_left_after_pay\":\"string单位(分)(选填)\"" +
            "}}")
    @PostMapping("/posOrderSync")
    @SystemControllerLog(description = "POS订单同步")
    public Map<String, String> posOrderSync(@RequestBody Map<String, String> params) {
        logger.info(params.toString());
        JSONObject payContent = new JSONObject();
        JSONObject retObj = new JSONObject();

        // 1. check sign
        Map<String, String> retMap = checkSign(params, payContent);
        if (retMap != null) {
            // 签名相关异常返回
            return retMap;
        }
        String resKey = payContent.getString(WxPayConstant.RES_KEY);
        logger.info(WxPayConstant.RES_KEY + ": " + resKey);

        try {
            retObj = posService.posOrderSync(params);
        } catch (MallinkException e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "", 500, e.getMessage());
        }

        return buildReturnMap(retObj, resKey, WxPayConstant.RET_SUCCESS, "",null);
    }

    /**
     * 构造返回map，无签名, 有错误
     * @param retCode
     * @param retMsg
     * @param errCode
     * @param errMsg
     * @return
     */
    private Map<String, String> buildReturnMap(String retCode, String retMsg, Integer errCode, String errMsg) {
        Map<String, String> retMap = new HashMap<>();
        retMap.put(WxPayConstant.RETURN_CODE, retCode);
        retMap.put(WxPayConstant.RETURN_MSG, retMsg);
        retMap.put(WxPayConstant.RESULT_CODE, "" + errCode);
        retMap.put(WxPayConstant.RESULT_MSG, errMsg);
        return retMap;
    }

    /**
     * 构造返回map，带签名, 有错误
     * @param dataMap
     * @param resKey
     * @param retCode
     * @param retMsg
     * @param err
     * @return
     */
    private Map<String, String> buildReturnMap(JSONObject dataMap, String resKey, String retCode, String retMsg, ErrorCode err) {
        Map<String, String> retMap = new HashMap<>();
        retMap.put(WxPayConstant.RETURN_CODE, retCode);
        retMap.put(WxPayConstant.RETURN_MSG, retMsg);
        if (err != null) {
            retMap.put(WxPayConstant.RESULT_CODE, "" + err.getCode());
            retMap.put(WxPayConstant.RESULT_MSG, err.getMessage());
        } else {
            retMap.put(WxPayConstant.RESULT_CODE, "0");
            retMap.put(WxPayConstant.RESULT_MSG, "");
        }
        if (dataMap != null) {
            retMap.put(WxPayConstant.RESULT_DATA, JSON.toJSONString(dataMap));
        }
        return WxPayment.buildSignAfterParasMapForHMAC(retMap, resKey);
    }

    /**
     * 构造返回map，带签名, 有错误
     * @param dataMap
     * @param resKey
     * @param retCode
     * @param retMsg
     * @param errCode
     * @param errMsg
     * @return
     */
    private Map<String, String> buildReturnMap(JSONObject dataMap, String resKey, String retCode, String retMsg, Integer errCode, String errMsg) {
        Map<String, String> retMap = new HashMap<>();
        retMap.put(WxPayConstant.RETURN_CODE, retCode);
        retMap.put(WxPayConstant.RETURN_MSG, retMsg);
        retMap.put(WxPayConstant.RESULT_CODE, "" + errCode);
        retMap.put(WxPayConstant.RESULT_MSG, errMsg);
        retMap.put(WxPayConstant.RESULT_DATA, JSON.toJSONString(dataMap));
        return WxPayment.buildSignAfterParasMapForHMAC(retMap, resKey);
    }


    /**
     * 验证创建订单请求参数,参数通过返回JSONObject对象,否则返回错误文本信息
     * @param params
     * @return
     */
    private Map<String, String> checkSign(Map<String, String> params, JSONObject payContent) {
        // 1. check sign
        // 开发者参数
        String devId = params.get(WxPayConstant.DEV_ID); // 开发者ID
        String sign  = params.get(WxPayConstant.SIGN); 	 // 签名
        if (StringUtils.isBlank(devId)) {
            String errorMessage = String.format("request params[%s] error.", WxPayConstant.DEV_ID);
            return buildReturnMap(WxPayConstant.RET_FAIL, "参数错误", ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), errorMessage);
        }
        if (StringUtils.isBlank(sign)) {
            String errorMessage = String.format("request params[%s] error.", WxPayConstant.SIGN);
            return buildReturnMap(WxPayConstant.RET_FAIL, "参数错误", ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), errorMessage);
        }

        // 查询开发者信息
        ThDevInfo devInfo = thDevInfoService.getByDevId(devId);
        if (devInfo == null) {
            String errorMessage = String.format("Can't found devInfo[%s=%s] record in db.", WxPayConstant.DEV_ID, devId);
            return buildReturnMap(WxPayConstant.RET_FAIL, "参数错误", ErrorCode.DEV_ID_NOT_FOUND.getCode(), errorMessage);
        }
        if (devInfo.getState().equals(EnumEnableType.Disable.getCode())) {
            String errorMessage = String.format("devInfo not available [%s=%s] record in db.", WxPayConstant.DEV_ID, devId);
            return buildReturnMap(WxPayConstant.RET_FAIL, "参数错误", ErrorCode.DEV_ID_IS_DISABLED.getCode(), errorMessage);
        }

        if (StringUtils.isBlank(devInfo.getReqKey())) {
            String errorMessage = String.format("%s is null[%s=%s] record in db.", WxPayConstant.REQ_KEY, WxPayConstant.DEV_ID, devId);
            return buildReturnMap(WxPayConstant.RET_FAIL, "参数错误", ErrorCode.DEV_ID_REQ_KEY_IS_NULL.getCode(), errorMessage);
        }
        if (StringUtils.isBlank(devInfo.getResKey())) {
            String errorMessage = String.format("%s is null[%s=%s] record in db.", WxPayConstant.RES_KEY, WxPayConstant.DEV_ID, devId);
            return buildReturnMap(WxPayConstant.RET_FAIL, "参数错误", ErrorCode.DEV_ID_RES_KEY_IS_NULL.getCode(), errorMessage);
        }
        payContent.put(WxPayConstant.RES_KEY, devInfo.getResKey());

        boolean signVerified = WxPayment.verifyNotifyHMAC(params, devInfo.getReqKey());
        if (!signVerified) {
            String errorMessage = ErrorCode.DEV_ID_SIGN_ERR.getMessage();
            logger.error(errorMessage + ": " + params.toString());
            return buildReturnMap(WxPayConstant.RET_FAIL, errorMessage, ErrorCode.DEV_ID_SIGN_ERR.getCode(), errorMessage + ": " + params.toString());
        }
        return null;
    }



}
