package com.jeequan.jeepay.mgr.ctrl.qrcode;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.icbc.api.internal.apache.http.impl.cookie.S;
import com.jeequan.jeepay.bizcommons.manage.qrshell.AbstractGenerator;
import com.jeequan.jeepay.bizcommons.manage.qrshell.ShellQRGenerator;
import com.jeequan.jeepay.core.aop.MethodLog;
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.model.DBApplicationConfig;
import com.jeequan.jeepay.core.model.QRCodeParams;
import com.jeequan.jeepay.core.model.export.MchQrCodesExportExcel;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.ExcelUtil;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.mgr.ctrl.CommonCtrl;
import com.jeequan.jeepay.service.impl.MchApplymentService;
import com.jeequan.jeepay.service.impl.MchQrcShellService;
import com.jeequan.jeepay.service.impl.MchQrcodeCardService;
import com.jeequan.jeepay.service.impl.SysConfigService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 商户应用管理类
 *
 * @author zhuxiao
 *
 * @date 2021-06-16 09:15
 */
@RestController
@RequestMapping("/api/mchQrCodes")
public class MchQrCodeController extends CommonCtrl {

    @Autowired private MchQrcodeCardService mchQrcodeCardService;
    @Autowired private SysConfigService sysConfigService;
    @Autowired private MchQrcShellService mchQrcShellService;
    @Autowired private MchApplymentService mchApplymentService;

    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_LIST')")
    @GetMapping
    public ApiRes list() {

        // 构造查询条件
        MchQrcodeCard mchQrCode = getObject(MchQrcodeCard.class);
        //mchQrCode.setQrcBelongType(MchQrcodeCard.QRC_BELONG_TYPE_ISV_PRE); // 只查询 下发二维码
        IPage<MchQrcodeCard> records = mchQrcodeCardService.selectPageRecords(getIPage(), mchQrCode);

        String payHostUrl = sysConfigService.getDBApplicationConfig().getPaySiteUrl();

        for (MchQrcodeCard record : records.getRecords()) {
            record.addExt("payHostUrl", payHostUrl);
        }
        return ApiRes.ok(records);
    }


    /** 查询当前批次号的数量 **/
    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_ADD')")
    @GetMapping("/batchIdDistinctCount")
    public ApiRes selectBatchIdDistinctCount() {

        Date now = new Date();
        Integer count = mchQrcodeCardService.getBaseMapper().selectBatchIdDistinctCount(DateKit.getBegin(now), DateKit.getEnd(now));

        // 时间格式化两位年 + 月  + 日
        String nowStr = DateUtil.format(now, "yyMMdd");

        // 左补两位0（如果不足）
        return ApiRes.ok(nowStr + String.format("%02d", ++count));
    }




    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_ADD')")
    @MethodLog(remark = "新建预制二维码")
    @PostMapping
    public ApiRes add() {

        // 处理金额类型
        handleParamAmount("fixedPayAmount");

        MchQrcodeCard mchQrCode = getObject(MchQrcodeCard.class);

        if(StringUtils.isEmpty(mchQrCode.getBatchId())){
            throw new BizException("请录入批次号");
        }

        try {
            Long.parseLong(mchQrCode.getBatchId());
        }catch (Exception e){
            throw new BizException("批次号格式不正确");
        }

        if(mchQrcodeCardService.count(MchQrcodeCard.gw().eq(MchQrcodeCard::getBatchId, mchQrCode.getBatchId())) > 0){
            throw new BizException("批次号已存在，请重新填写");
        }

        mchQrCode.setBindState(CS.NO); // 未绑定

        int addNum = mchQrCode.extv().getInteger("addNum");

        if(addNum > 9999){
            throw new BizException("新建数量不能超过9999");
        }

        for (int i = 0; i < addNum; i++) {
            mchQrCode.setQrcId( Long.parseLong(String.format("%s%04d", mchQrCode.getBatchId(), (i + 1) ) )); // 避免 赋值ID
            boolean result = mchQrcodeCardService.addQrCode(mchQrCode, MchQrcodeCard.QRC_BELONG_TYPE_ISV_PRE);
            if (!result) {
                return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_CREATE);
            }
        }

        return ApiRes.ok();
    }

    @PreAuthorize("hasAnyAuthority('ENT_DEVICE_QRC_VIEW', 'ENT_DEVICE_QRC_EDIT')")
    @GetMapping("/{recordId}")
    public ApiRes detail(@PathVariable("recordId") Long recordId) {

        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(recordId);

        if (dbRecord == null) {
            return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_SEARCH);
        }
        return ApiRes.ok(dbRecord);
    }

    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_EDIT')")
    @MethodLog(remark = "更新二维码信息")
    @PutMapping("/{recordId}")
    public ApiRes update(@PathVariable("recordId") Long recordId) {

        // 处理金额类型
        handleParamAmount("fixedPayAmount");

        MchQrcodeCard mchQrCode = getObject(MchQrcodeCard.class);
        mchQrCode.setBatchId(null); // 批次号不允许变更
        mchQrCode.setQrcId(recordId);
        mchQrCode.setCreatedAt(null);
        mchQrCode.setUpdatedAt(null);

        boolean result = mchQrcodeCardService.updateById(mchQrCode);
        if (!result) {
            return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_UPDATE);
        }
        return ApiRes.ok();
    }

    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_DEL')")
    @MethodLog(remark = "删除二维码")
    @DeleteMapping
    public ApiRes delete() {

        String delQrcIds = getValStringRequired("delQrcIds");

        List<String> qrcIdList = Arrays.stream(delQrcIds.split(",")).collect(Collectors.toList());

        long count = mchQrcodeCardService.count(MchQrcodeCard.gw()
                .eq(MchQrcodeCard::getBindState, CS.YES)
                .in(MchQrcodeCard::getQrcId, qrcIdList)
        );
        if (count > 0) {
            return ApiRes.customFail("码牌已绑定商户，不可删除！");
        }

        if(mchQrcodeCardService.removeByIds(qrcIdList)) {
            return ApiRes.ok();
        }

        return ApiRes.ok();
    }

    /**
     * @author: xiaoyu
     * @date: 2022/1/14 9:45
     * @describe: 订单列表数据导出
     */
    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_EXPORT')")
    @RequestMapping(value="/exportExcel", method = RequestMethod.GET)
    public void exportExcel() throws Exception{

        String exportModel = getValStringRequired("exportModel");
        MchQrcodeCard mchQrcodeCard = getObject(MchQrcodeCard.class);
        LambdaQueryWrapper<MchQrcodeCard> wrapper = MchQrcodeCard.gw();

        if(mchQrcodeCardService.countByWrapper(mchQrcodeCard) > 500){
            throw new BizException("导出数量不可超过500");
        }

        IPage<MchQrcodeCard> pages = mchQrcodeCardService.listByPage(getIPage(true), mchQrcodeCard, wrapper);

        // 查询当前系统配置信息
        DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();

        // Excel: 信息和url
        if ("infoAndUrl".equals(exportModel)) {
                try {
                    List<JSONObject> newList = new LinkedList<>();
                    for (MchQrcodeCard qrcodeCard:pages.getRecords()) {
                        MchStore mchStore = mchStoreService.getById(qrcodeCard.getStoreId());
                        if(StringUtils.isNotEmpty(qrcodeCard.getStoreId()) &&  !"0".equals(qrcodeCard.getStoreId())){
                            qrcodeCard.setStoreName(mchStore.getStoreName());
                        }
                        MchApplyment applyment = mchApplymentService.getById(qrcodeCard.getMchApplyId());
                        JSONObject object = (JSONObject) JSONObject.toJSON(qrcodeCard);
                        object.put("fixedPayAmount", AmountUtil.convertCent2Dollar(qrcodeCard.getFixedPayAmount()));
                        object.put("qrCodeUrl", dbApplicationConfig.genUniJsapiPayUrl(QRCodeParams.TYPE_QRC, qrcodeCard.getEntryPage(), qrcodeCard.getQrcId() + "", applyment.getIsvNo()));
                        newList.add(object);
                    }

                    List<MchQrCodesExportExcel> linkedList = JSONArray.parseArray(JSONArray.toJSONString(newList), MchQrCodesExportExcel.class);
                    // excel输出
                    ExcelUtil.exportExcel(linkedList, "码牌", "码牌", MchQrCodesExportExcel.class, "码牌", response);
                }catch (Exception e) {
                    logger.error("导出excel失败", e);
                    throw new BizException("导出二维码失败！");
                }

            // 模板图片 || 纯二维码  || 二维码包含ID
            }else if ("templateImg".equals(exportModel) || "qrcode".equals(exportModel) || "qrcodeAndQrcId".equals(exportModel) ) {
                //文件的名称
                String downloadFilename = "qrcode_" + DateUtil.today() + ".zip";
                //设置格式
                response.setCharacterEncoding("UTF-8");
                response.setHeader("content-Type", "application/x-msdownload");
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(downloadFilename, "UTF-8"));

                ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());

                // 查询所有的模板数据
                Map<Long, MchQrcShell> map = new HashMap<>();
                pages.getRecords().forEach(r -> map.put(r.getQrcShellId(), null));
                if(!map.keySet().isEmpty()){
                    mchQrcShellService.list(MchQrcShell.gw().in(MchQrcShell::getSid, map.keySet())).forEach(r -> {
                        map.put(r.getSid(), r);
                    });
                }

                for (MchQrcodeCard qrcodeCard:pages.getRecords()) {

                    String mchStoreName = "";
                    if(qrcodeCard.getBindState() == CS.YES && qrcodeCard.getStoreId() != null && !"-1".equals(qrcodeCard.getStoreId())){
                        MchStore mchStore = mchStoreService.getById(qrcodeCard.getStoreId());
                        mchStoreName = mchStore != null ? mchStore.getStoreName() : "";
                    }

                    BufferedImage bufferedImage = null;

                    MchQrcShell mchQrcShell = map.get(qrcodeCard.getQrcShellId());

                    AbstractGenerator abstractGenerator = SpringBeansUtil.getBean(ShellQRGenerator.class);
                    if(mchQrcShell != null && "templateImg".equals(exportModel)){ //存在模板 && 显式导出模板
                        abstractGenerator = getAbstractGenerator(mchQrcShell.getStyleCode());
                    }

                    String configStr = null;
                    if("templateImg".equals(exportModel)){ // 模板图片
                        if(mchQrcShell != null){
                            configStr = mchQrcShell.getConfigInfo();
                        }
                    }else if("qrcode".equals(exportModel)){ // 纯二维码（不包含ID）

                        configStr = "{showIdFlag: false}";

                    }else if("qrcodeAndQrcId".equals(exportModel)){ // 二维码 + ID

                        configStr = "{showIdFlag: true}";
                    }
                    MchApplyment applyment = mchApplymentService.getById(qrcodeCard.getMchApplyId());

                    bufferedImage = abstractGenerator
                            .genQrImgBuffer(configStr,
                                    dbApplicationConfig.genUniJsapiPayUrl(QRCodeParams.TYPE_QRC, qrcodeCard.getEntryPage(), qrcodeCard.getQrcId() + "", applyment.getIsvNo()),
                                    qrcodeCard.getQrcId(),
                                    mchStoreName,
                                    false
                            );

                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    ImageIO.write(bufferedImage, "png", out);
                    InputStream is = new ByteArrayInputStream(out.toByteArray());
                    zos.putNextEntry(new ZipEntry( qrcodeCard.getQrcId() + ".png"));
                    byte[] buffer = new byte[1024];
                    int r = 0;
                    while ((r = is.read(buffer)) != -1) {
                        zos.write(buffer, 0, r);
                    }
                    is.close();
                    is = null;
                    out.close();
                    out = null;
                    zos.flush();
                }

                zos.flush();
                zos.close();

            }
    }

    /**
     * @author: zx
     * @date: 2021-12-28 09:15
     * @describe: 绑定码牌
     */
    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_EDIT')")
    @PutMapping("/bind/{recordId}")
    public ApiRes bind(@PathVariable("recordId") Long recordId) {

        String mchNo = getValStringRequired("mchNo");
        String appId = getValStringRequired("appId");
        String storeId = getValStringRequired("storeId");
        String mchApplyId=getValStringRequired("mchApplyId");

        MchInfo mchInfo = mchInfoService.getById(mchNo);
        if (mchInfo == null) {
            throw new BizException("商户不存在");
        }

        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(recordId);
        if (StringUtils.isNotBlank(dbRecord.getAgentNo()) && !dbRecord.getAgentNo().equals(mchInfo.getAgentNo())) {
            throw new BizException("选择的商户不属于设备已划拨的服务商，请收回设备或绑定其他商户");
        }

        MchQrcodeCard updateRecord = new MchQrcodeCard();
        updateRecord.setQrcId(recordId).setMchNo(mchNo).setStoreId(storeId).setAppId(appId).setMchApplyId(mchApplyId).setBindState(CS.YES).setAgentNo(mchInfo.getAgentNo());

        mchQrcodeCardService.bindEmptyQR(updateRecord);

        return ApiRes.ok();
    }

    /**
     * @Author: zx
     * @Description: 解绑码牌
     * @Date: 15:59 2022/5/30
     */
    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_EDIT')")
    @PostMapping("/unbind/{qrcId}")
    public ApiRes unbind(@PathVariable("qrcId") Long qrcId) {

        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(qrcId);
        if (dbRecord == null) {
            return ApiRes.customFail("解绑失败");
        }

        mchQrcodeCardService.unbind(dbRecord);

        return ApiRes.ok();
    }

    /**
     * @Author: zx
     * @Description: 查看码牌绑定的设备列表
     * @Date: 15:59 2022/5/30
     */
    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_EDIT')")
    @GetMapping("/bindDevice/{qrcId}")
    public ApiRes bindDevice(@PathVariable("qrcId") Long qrcId) {

        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(qrcId);
        if (dbRecord == null) {
            return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_SEARCH);
        }

        IPage<MchStoreDevice> page = mchQrcodeCardService.getBindDevice(getIPage(), dbRecord, null);

        return ApiRes.page(page);
    }

    /**
     * @author: zx
     * @date: 2021-12-28 09:15
     * @describe: 划拨码牌给服务商
     */
    @PreAuthorize("hasAuthority('ENT_DEVICE_QRC_ALLOT')")
    @PostMapping("/allotQrc")
    public ApiRes allotQrc() {

        String allotType = getValStringRequired("allotType"); // 划拨类型：select-勾选划拨  batch-批次划拨  range-区间划拨
        String allotOrRecover = getValStringRequired("allotOrRecover"); // 分配类型：allot-划拨  recover-收回

        List<Long> qrcIdList = new LinkedList<>();

        // 构建批量更新设备ID集合
        if (allotType.equals("select")) {

            String allotds = getValStringRequired("allotIds");
            qrcIdList = Arrays.stream(allotds.split(",")).map(Long::parseLong).collect(Collectors.toList());

        }else if (allotType.equals("batch")) {

            String batchId = getValStringRequired("batchId");
            List<MchQrcodeCard> list = mchQrcodeCardService.list(MchQrcodeCard.gw().eq(MchQrcodeCard::getBatchId, batchId));
            if (CollUtil.isEmpty(list)) {
                throw new BizException("批次号错误");
            }
            for (MchQrcodeCard mchQrcodeCard : list) {
                qrcIdList.add(mchQrcodeCard.getQrcId());
            }

        } else if (allotType.equals("range")) {
            String startQrcId=getValStringRequired("startQrcId");
            String endQrcId=getValStringRequired("endQrcId");
            List<MchQrcodeCard> list = mchQrcodeCardService.list(MchQrcodeCard.gw().ge(MchQrcodeCard::getQrcId, startQrcId)
                    .le(MchQrcodeCard::getQrcId, endQrcId));
            if (CollUtil.isEmpty(list)) {
                throw new BizException("区间不存在相应码牌数据");
            }
            for (MchQrcodeCard mchQrcodeCard : list) {
                qrcIdList.add(mchQrcodeCard.getQrcId());
            }

        } else {
            throw new BizException("划拨类型错误");
        }

        if (CollUtil.isEmpty(qrcIdList)) {
            throw new BizException("请选择码牌");
        }

        // 1、收回码牌
        if (allotOrRecover.equals("recover")) {
            mchQrcodeCardService.recoverQrc(true, qrcIdList, null, null);
            return ApiRes.ok();
        }

        // 2、划拨码牌
        String agentNo = getValStringRequired("agentNo");
        List<String> mchNoList = getMchNoListByAgentNo(agentNo);

        mchQrcodeCardService.allotQrc(true, agentNo, qrcIdList, mchNoList, null);

        return ApiRes.ok();
    }


    /** 查询支付系统的地址 **/
    @GetMapping("/paySiteUrl")
    public ApiRes paySiteUrl() {
        String paySiteUrl = sysConfigService.getDBApplicationConfig().getPaySiteUrl();
        return ApiRes.ok(paySiteUrl);
    }

    /** 区间划拨码牌  提前校验接口 **/
    @PostMapping("/check4RangeAllot")
    public ApiRes check4RangeAllot() {
        List<MchQrcodeCard> qrcList = getRangeQrcList();
        List<Long> qrcIdList = qrcList.stream().map(MchQrcodeCard::getQrcId).collect(Collectors.toList());

        if (qrcIdList.size() <= 10) {
            return ApiRes.ok("将划拨【" + CollUtil.join(qrcIdList, ",") + "】共" + qrcIdList.size() + "个码牌");

        }else {
            String beforeStr = CollUtil.join(CollUtil.sub(qrcIdList, 0, 3), ",");
            String afterStr = CollUtil.join(CollUtil.sub(qrcIdList, qrcIdList.size() - 4, qrcIdList.size() - 1), ",");
            return ApiRes.ok("将划拨【" + beforeStr + "..." + afterStr + "】等共" + qrcIdList.size() + "个码牌");
        }
    }

    private AbstractGenerator getAbstractGenerator(String code){

        AbstractGenerator generator = SpringBeansUtil.getBean(code + "Generator", AbstractGenerator.class);
        if(generator == null){
            throw new BizException("选择模板[" + code + "]不存在！");
        }
        return generator;
    }


    /** 按码牌ID区间划拨码牌时，获取区间内的码牌ID列表 **/
    private List<MchQrcodeCard> getRangeQrcList(){

        try {
            Long.parseLong(getValStringRequired("startQrcId"));
            Long.parseLong(getValStringRequired("endQrcId"));
        }catch (Exception e){
            throw new BizException("起始或截止码牌ID格式不正确");
        }

        Long startQrcId = getValLongRequired("startQrcId");
        Long endQrcId = getValLongRequired("endQrcId");
        List<MchQrcodeCard> list = mchQrcodeCardService.list(MchQrcodeCard.gw().between(MchQrcodeCard::getQrcId, startQrcId, endQrcId).orderByAsc(MchQrcodeCard::getQrcId));
        if (CollUtil.isEmpty(list)) {
            throw new BizException("当前ID区间无码牌，请检查起止码牌ID是否正确");
        }

        return list;
    }


}
