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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
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.jeequan.jeepay.agent.ctrl.CommonCtrl;
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.ExcelUtil;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.service.impl.*;
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 MchQrcShellService mchQrcShellService;
    @Autowired private MchInfoService mchInfoService;
    @Autowired private MchStoreService mchStoreService;
    @Autowired private MchAppService mchAppService;
    @Autowired private MchApplymentService mchApplymentService;

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

        // 构造查询条件
        MchQrcodeCard mchQrCode = getObject(MchQrcodeCard.class);

        // 可查看自己和全部下级代理的设备
        String currentAgentNo = getCurrentAgentNo();
        List<String> subAgentNoList = agentInfoService.queryAllSubAgentNo(currentAgentNo);
        // 自己和下级服务商
        if (CollUtil.isNotEmpty(subAgentNoList)){
            mchQrCode.setSubAgentNoList(subAgentNoList);
        }
        IPage<MchQrcodeCard> page = mchQrcodeCardService.selectPageRecords(getIPage(), mchQrCode);
        if (page.getTotal() == 0) {
            return ApiRes.page(page);
        }

        // 列表添加是否属于自身，设备只能绑定直属商户，app端展示隐藏也使用此字段
        for (MchQrcodeCard qrcodeCard: page.getRecords()) {
            if (currentAgentNo.equals(qrcodeCard.getAgentNo())) {
                qrcodeCard.addExt("isSelf", true);
            }
        }

        return ApiRes.page(page);
    }


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

        // 可查看自己和全部下级代理的码牌
        List<String> subAgentNoList = agentInfoService.queryAllSubAgentNo(getCurrentAgentNo());
        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(recordId);
        if (!subAgentNoList.contains(dbRecord.getAgentNo())) {
            return ApiRes.fail(ApiCodeEnum.SYS_PERMISSION_ERROR);
        }

        // 设备是否属于自身
        if (getCurrentAgentNo().equals(dbRecord.getAgentNo())) {
            dbRecord.addExt("isSelf", true);
        }

        // 添加服务商、商户、门店、应用名称
        AgentInfo allotAgent = agentInfoService.getById(dbRecord.getAgentNo());
        if (allotAgent != null) {
            dbRecord.addExt("agentName", allotAgent.getAgentName());
        }
        if (dbRecord.getBindState() == CS.YES) {
            if (StringUtils.isNotBlank(dbRecord.getMchNo())) {
                MchInfo mchInfo = mchInfoService.getById(dbRecord.getMchNo());
                if (mchInfo != null) {
                    dbRecord.addExt("mchName", mchInfo.getMchName());
                }
            }

            if (dbRecord.getStoreId() != null) {
                MchStore mchStore = mchStoreService.getById(dbRecord.getStoreId());
                if (mchStore != null) {
                    dbRecord.addExt("storeName", mchStore.getStoreName());
                }
            }

            if (StringUtils.isNotBlank(dbRecord.getAppId())) {
                MchAppEntity mchAppEntity = mchAppService.getById(dbRecord.getAppId());
                if (mchAppEntity != null) {
                    dbRecord.addExt("appName", mchAppEntity.getAppName());
                }
            }
        }
        MchApplyment mchApplyment = mchApplymentService.getById(dbRecord.getMchApplyId());
        // 封装URL
        dbRecord.addExt("qrUrl", sysConfigService.getDBApplicationConfig().genUniJsapiPayUrl(QRCodeParams.TYPE_QRC, dbRecord.getEntryPage(), recordId + "", mchApplyment.getIsvNo()));
        return ApiRes.ok(dbRecord);
    }

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

        // 可查看自己和全部下级代理的码牌
        List<String> subAgentNoList = agentInfoService.queryAllSubAgentNo(getCurrentAgentNo());
        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(recordId);
        if (!subAgentNoList.contains(dbRecord.getAgentNo())) {
            return ApiRes.fail(ApiCodeEnum.SYS_PERMISSION_ERROR);
        }

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

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

        // 解绑同时设置商户号为空串，设置门店ID为-1
        if (mchQrCode.getBindState() != null && mchQrCode.getBindState() == CS.NO) {
            mchQrCode.setMchNo("");
            mchQrCode.setAppId("");
            mchQrCode.setStoreId("-1");
        }

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



    /** 解析二维码 && 验证二维码是否可绑定 **/
    @GetMapping("/parseQrCodeUrl")
    public ApiRes parseQrCodeUrl() {

        String url = getValStringRequired("qrUrl");

        // 不是支付平台开头的， 说明不是可用二维码
        if(!url.startsWith(sysConfigService.getDBApplicationConfig().getPaySiteUrl())){
            throw new BizException("二维码解析失败");
        }

        int tokenIndex = url.indexOf(JeepayKit.TOKEN_KEY + "=");
        if(tokenIndex <= 0){
            throw new BizException("二维码解析失败");
        }

        String token = url.substring(tokenIndex + 12);

        QRCodeParams qrCodeParams = JSON.parseObject(JeepayKit.aesDecode(token), QRCodeParams.class); //解析token

        MchQrcodeCard mchQrcodeCard = mchQrcodeCardService.getById( qrCodeParams.getId());

        // 不存在  不可用 已绑定
        if(mchQrcodeCard == null || mchQrcodeCard.getQrcState() != CS.YES || mchQrcodeCard.getBindState() != CS.NO){
            throw new BizException("二维码不存在");
        }

        return ApiRes.ok(mchQrcodeCard.getQrcId());
    }

    /**
     * @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");
        }

        // 服务商号条件不为空，搜索该服务商号下
        wrapper.eq(StringUtils.isNotEmpty(mchQrcodeCard.getAgentNo()), MchQrcodeCard::getAgentNo, mchQrcodeCard.getAgentNo());
        List<String> subAgentNoList = agentInfoService.queryAllSubAgentNo(getCurrentAgentNo());
        // 自己和下级服务商
        wrapper.in(CollUtil.isNotEmpty(subAgentNoList), MchQrcodeCard::getAgentNo, subAgentNoList);
        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()) {
                    MchApplyment mchApplyment = 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() + "", mchApplyment.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().stream().forEach(r -> map.put(r.getQrcShellId(), null));
            if(!map.keySet().isEmpty()){
                mchQrcShellService.list(MchQrcShell.gw().in(MchQrcShell::getSid, map.keySet())).stream().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: 绑定码牌
     */
    @MethodLog(remark = "绑定码牌")
    @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");

        MchInfo mchInfo = mchInfoService.getById(mchNo);
        if (mchInfo == null || StringUtils.isBlank(mchInfo.getAgentNo()) || !mchInfo.getAgentNo().equals(getCurrentAgentNo())) {
            throw new BizException("商户不存在");
        }

        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(recordId);
        if (dbRecord == null || StringUtils.isBlank(dbRecord.getAgentNo()) || !dbRecord.getAgentNo().equals(getCurrentAgentNo())) {
            throw new BizException(ApiCodeEnum.SYS_OPERATION_FAIL_SEARCH);
        }

        MchQrcodeCard updateRecord = new MchQrcodeCard();
        updateRecord.setQrcId(recordId).setMchNo(mchNo).setStoreId(storeId).setAppId(appId).setBindState(CS.YES);

        mchQrcodeCardService.bindEmptyQR(updateRecord);

        return ApiRes.ok();
    }

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

        MchQrcodeCard dbRecord = mchQrcodeCardService.getById(qrcId);
        if (dbRecord == null || !dbRecord.getAgentNo().equals(getCurrentAgentNo())) {
            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 || !dbRecord.getAgentNo().equals(getCurrentAgentNo())) {
            return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_SEARCH);
        }

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

        return ApiRes.page(page);
    }

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

        // 服务商是否支持划拨码牌、设备
        SysConfig isSupportAgentAllot = sysConfigService.getById("isSupportAgentAllot");
        if(isSupportAgentAllot == null || String.valueOf(CS.NO).equals(isSupportAgentAllot.getConfigVal())){
            throw  new BizException("不支持服务商划拨码牌、设备");
        }

        String allotType = getValStringRequired("allotType"); // 划拨类型：select-勾选划拨  batch-批次划拨
        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 {
            throw new BizException("划拨类型错误");
        }

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

        // 只能操作下级代理
        List<String> subAgentNoList = agentInfoService.queryAllSubAgentNo(getCurrentAgentNo());

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

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

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

        return ApiRes.ok();
    }

    private AbstractGenerator getAbstractGenerator(String code){

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

}
