package com.cpop.oam.business.controller.backstage;

import cn.binarywang.wx.miniapp.bean.WxMaAuditMediaUploadResult;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.R;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.oam.business.bo.WxOpenMaCodeCommitBo;
import com.cpop.oam.business.bo.WxOpenMaTrialQrCodeBo;
import com.cpop.oam.business.service.WxOpenMiniService;
import com.cpop.oam.business.vo.WxOpenMiniVo;
import com.cpop.oam.framework.config.wxOpen.WxOpenProperties;
import com.cpop.oam.framework.handler.wxOpen.WxOpenService;
import com.mybatisflex.core.paginate.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate;
import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage;
import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
import me.chanjar.weixin.open.bean.result.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.List;

/**
 * @author DB
 * @createTime 2023/10/10 13:40
 * @description 微信开放平台
 */
@RestController
@Api(tags = "微信开放平台模块")
@RequestMapping("/backstage/wxOpen")
public class BackstageWxOpenController {

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

    @Autowired
    private WxOpenService wxOpenService;

    @Autowired
    private WxOpenProperties properties;

    @Autowired
    private WxOpenMiniService wxOpenMiniService;

    /**
     * @descriptions 第三方平台授权
     * @author DB
     * @date 2023/10/10 13:43
     * @param requestBody 请求参数
     * @param timestamp 时间戳
     * @param nonce 随机串
     * @param signature 签名
     * @param encType 解密类型
     * @param msgSignature 签名信息
     * @return: java.lang.Object 返回
     */
    @RequestMapping("/receiveTicket")
    public Object receiveTicket(@RequestBody(required = false) String requestBody, @RequestParam("timestamp") String timestamp,
                                @RequestParam("nonce") String nonce, @RequestParam("signature") String signature,
                                @RequestParam(name = "encrypt_type", required = false) String encType,
                                @RequestParam(name = "msg_signature", required = false) String msgSignature) {
        if (!StringUtils.equalsIgnoreCase("aes", encType) || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
            throw new ServiceException("非法请求，可能属于伪造的请求！");
        }
        // aes加密的消息
        WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody, wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
        try {
            wxOpenService.getWxOpenComponentService().route(inMessage);
        } catch (WxErrorException e) {
            this.logger.error("wxOpen:receiveTicket", e);
        }
        return "success";
    }

    /**
     * @Description: 获取授权链接
     * @return R<String>
     * @Author DB
     * @Date: 2023/6/14 21:47
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:insert')")
    @ApiOperation("获取授权链接")
    @GetMapping("/getAuthorizedLink")
    public R<String> getAuthorizedLink() {
        String preAuthUrl;
        try {
            preAuthUrl = wxOpenService.getWxOpenComponentService().getPreAuthUrl(properties.getRedirectUri());
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok(preAuthUrl);
    }

    /**
     * @descriptions 授权回调地址
     * @author DB
     * @date 2023/10/10 15:49
     * @param authorizationCode 授权码
     * @return: me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult
     */
    @GetMapping("/redirectUrl")
    @ResponseBody
    public WxOpenQueryAuthResult redirectUrl(@RequestParam("auth_code") String authorizationCode) {
        try {
            return wxOpenService.getWxOpenComponentService().getQueryAuth(authorizationCode);
        } catch (WxErrorException e) {
            logger.error("gotoPreAuthUrl", e);
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @descriptions
     * @author DB
     * @date 2023/10/10 14:56
     * @param requestBody 请求体
     * @param appId appid
     * @param signature 签名
     * @param timestamp 时间戳
     * @param nonce 随机串
     * @param openid openId
     * @param encType 加密类型
     * @param msgSignature 加密信息
     * @return: java.lang.Object
     */
    @RequestMapping("/{appId}/callback")
    public Object callback(@RequestBody(required = false) String requestBody,
                           @PathVariable("appId") String appId,
                           @RequestParam("signature") String signature,
                           @RequestParam("timestamp") String timestamp,
                           @RequestParam("nonce") String nonce,
                           @RequestParam("openid") String openid,
                           @RequestParam("encrypt_type") String encType,
                           @RequestParam("msg_signature") String msgSignature) {
        if (!StringUtils.equalsIgnoreCase("aes", encType) || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
            throw new ServiceException("非法请求，可能属于伪造的请求！");
        }
        String out = "";
        // aes加密的消息
        WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(requestBody, wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
        // 全网发布测试用例
        if (StringUtils.equalsAnyIgnoreCase(appId, "wxd101a85aa106f53e", "wx570bc396a51b8ff8")) {
            try {
                if (StringUtils.equals(inMessage.getMsgType(), "text")) {
                    if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
                        out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
                                WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
                                        .fromUser(inMessage.getToUser())
                                        .toUser(inMessage.getFromUser())
                                        .build(),
                                wxOpenService.getWxOpenConfigStorage()
                        );
                    } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
                        String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
                        WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
                        wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
                    }
                } else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
                    WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
                    wxOpenService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
                }
            } catch (WxErrorException e) {
                logger.error("callback", e);
            }
        }else{
            WxMpXmlOutMessage outMessage = wxOpenService.getWxOpenMessageRouter().route(inMessage, appId);
            if(outMessage != null){
                out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMessage, wxOpenService.getWxOpenConfigStorage());
            }
        }
        return out;
    }

    /**
     * @param appid 绑定小程序appid
     * @return R<String>
     * @Description: 小程序绑定开放平台
     * @Author DB
     * @Date: 2023/6/14 21:47
     */
    @ApiOperation("小程序绑定开放平台")
    @GetMapping("/bindOpenAccount/{appid}")
    public R<Void> bindOpenAccount(@PathVariable String appid) {
        try {
            wxOpenService.getWxOpenComponentService().bindOpenAccount(appid, WxConsts.AppIdType.MINI_TYPE, properties.getOpenAppid());
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @Description: 解除绑定开放平台帐号
     * @param appid 绑定小程序appid
     * @return R<String>
     * @Author DB
     * @Date: 2023/6/14 21:47
     */
    @ApiOperation("解除绑定开放平台帐号")
    @GetMapping("/unbindOpenAccount/{appid}")
    public R<Void> unbindOpenAccount(@PathVariable String appid) {
        try {
            wxOpenService.getWxOpenComponentService().unbindOpenAccount(appid, WxConsts.AppIdType.MINI_TYPE, properties.getOpenAppid());
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @return R<Page<Map<String, String>>>
     * @Description: 获取授权小程序列表
     * @author DB
     * @Date: 2023/6/14 0014 17:00
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:list')")
    @ApiOperation("获取授权小程序列表")
    @GetMapping("/getAuthorizerList")
    public R<Page<WxOpenMiniVo>> getAuthorizerList() {
        try {
            PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
            WxOpenAuthorizerListResult authorizerList = wxOpenService.getWxOpenComponentService().getAuthorizerList((pageDomain.getPageNum() - 1) * pageDomain.getPageSize(), pageDomain.getPageSize());
            List<WxOpenMiniVo> list = wxOpenMiniService.getAuthorizerList(authorizerList);
            Page<WxOpenMiniVo> page = Page.of(pageDomain.getPageNum(), pageDomain.getPageSize(), authorizerList.getTotalCount());
            page.setRecords(list);
            return R.ok(page);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @Description: 数据同步
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("数据同步")
    @PutMapping("/dataSync/{authorizerAppid}")
    public R<Void> dataSync(@PathVariable("authorizerAppid") String authorizerAppid) {
        try {
            WxOpenAuthorizerInfoResult authorizerInfo = wxOpenService.getWxOpenComponentService().getAuthorizerInfo(authorizerAppid);
            wxOpenMiniService.dataSync(authorizerInfo);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @Description: 获取授权小程序详情
     * @param authorizerAppid 授权小程序appid
     * @return R<List<OpenPlatformAuthorizerListVo>>
     * @author DB
     * @Date: 2023/6/15 0015 16:38
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:info')")
    @ApiOperation("获取授权小程序详情")
    @GetMapping("/getAuthorizerInfo/{authorizerAppid}")
    public R<WxOpenAuthorizerInfoResult> getAuthorizerInfo(@PathVariable String authorizerAppid) {
        try {
            WxOpenAuthorizerInfoResult wxOpenAuthorizerInfoResult = wxOpenService.getWxOpenComponentService().getAuthorizerInfo(authorizerAppid);
            return R.ok(wxOpenAuthorizerInfoResult);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @Description: 上传代码并生成体验版
     * @param bo 请求参数
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("上传代码并生成体验版")
    @PostMapping("/commit")
    public R<Void> commit(@RequestBody @Validated WxOpenMaCodeCommitBo bo) {
        try {
            wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(bo.getAuthorizerAppid())
                    .codeCommit(bo.getTemplateId(),bo.getUserVersion(),bo.getUserDesc(),bo.getExtJson());
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @Description: 获取体验版二维码
     * @param bo 请求参数
     * @return R<JSONObject>
     * @author DB
     * @Date: 2023/6/16 0016 17:35
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:info')")
    @ApiOperation("获取体验版二维码")
    @PostMapping("/getTrialQrCode")
    public R<String> getTrialQrCode(@RequestBody WxOpenMaTrialQrCodeBo bo) {
        try {
            File testQrcode = wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(bo.getAuthorizerAppid())
                    .getTestQrcode(bo.getPath(), null);
            FileInputStream inputFile = new FileInputStream(testQrcode);
            BufferedImage bufferedImage = ImageIO.read(inputFile);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "png", outputStream);
            String trialQrCode = Base64.getEncoder().encodeToString(outputStream.toByteArray());
            String head = "data:image/png;base64,";
            return R.ok(head + trialQrCode);
        } catch (IOException | WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @Description: 提交代码审核
     * @param message 微信小程序代码包提交审核(仅供第三方开发者代小程序调用
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("提交代码审核")
    @PostMapping("/submitAudit/{authorizerAppid}")
    public R<WxOpenMaSubmitAuditResult> submitAudit(@PathVariable String authorizerAppid ,@RequestBody WxOpenMaSubmitAuditMessage message) {
        try {
            WxOpenMaSubmitAuditResult wxOpenMaSubmitAuditResult = wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid)
                    .submitAudit(message);
            return R.ok(wxOpenMaSubmitAuditResult);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @descriptions 查询审核单状态
     * @author DB
     * @date 2023/10/10 17:41
     * @param authorizerAppid 授权appid
     * @param auditId 审核id
     * @return: com.cpop.core.base.R<me.chanjar.weixin.open.bean.result.WxOpenMaQueryAuditResult>
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:info')")
    @ApiOperation("查询审核单状态")
    @GetMapping("/getAuditStatus")
    public R<WxOpenMaQueryAuditResult> getAuditStatus(@RequestParam("authorizerAppid") String authorizerAppid, @RequestParam("auditId") Long auditId) {
        try {
            WxOpenMaQueryAuditResult auditStatus = wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).getAuditStatus(auditId);
            return R.ok(auditStatus);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @Description: 撤回代码审核
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("撤回代码审核")
    @PutMapping("/undoAudit/{authorizerAppid}")
    public R<Void> getAuditStatus(@PathVariable String authorizerAppid) {
        try {
            wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).undoCodeAudit();
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @Description: 发布已通过审核的小程序
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("发布已通过审核的小程序")
    @PutMapping("/release/{authorizerAppid}")
    public R<Void> release(@PathVariable String authorizerAppid) {
        try {
            wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).releaseAudited();
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @Description: 撤回代码审核
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("小程序版本回退")
    @PutMapping("/revertCodeRelease/{authorizerAppid}")
    public R<Void> revertCodeRelease(@PathVariable String authorizerAppid) {
        try {
            wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).revertCodeRelease();
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @Description: 加急代码审核
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("加急代码审核")
    @PutMapping("/speedupCodeAudit")
    public R<Void> speedupCodeAudit(@RequestParam("authorizerAppid") String authorizerAppid, @RequestParam("auditId") Long auditId) {
        try {
            wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).speedAudit(auditId);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @return R<OpenPlatformVersionInfoVo>
     * @Description: 查询小程序版本信息
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:info')")
    @ApiOperation("查询小程序版本信息")
    @GetMapping("/getVersionInfo/{authorizerAppid}")
    public R<WxOpenVersioninfoResult> getVersionInfo(@PathVariable String authorizerAppid) {
        try {
            WxOpenVersioninfoResult versionInfo = wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).getVersionInfo();
            return R.ok(versionInfo);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @Description: 查询最新一次审核单状态
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:info')")
    @ApiOperation("查询最新一次审核单状态")
    @GetMapping("/getLatestAuditStatus/{authorizerAppid}")
    public R<WxOpenMaQueryAuditResult> getLatestAuditStatus(@PathVariable String authorizerAppid) {
        try {
            WxOpenMaQueryAuditResult latestAuditStatus = wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).getLatestAuditStatus();
            return R.ok(latestAuditStatus);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @return R<OpenPlatformVersionInfoVo>
     * @Description: 上传提审素材
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("上传提审素材")
    @PostMapping("/uploadMediaToCodeAudit/{authorizerAppid}")
    public R<WxMaAuditMediaUploadResult> uploadMediaToCodeAudit(@RequestParam("file") File file, @PathVariable("authorizerAppid") String authorizerAppid) {
        try {
            WxMaAuditMediaUploadResult wxMaAuditMediaUploadResult = wxOpenService.getWxOpenComponentService().getWxMaServiceByAppid(authorizerAppid).uploadMedia(file);
            return R.ok(wxMaAuditMediaUploadResult);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @descriptions 获取模板列表
     * @author DB
     * @date 2023/10/10 18:17
     * @param templateType 模板类型
     * @return: com.cpop.core.base.R<java.util.List<me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate>>
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:info')")
    @ApiOperation("获取模板列表")
    @GetMapping("/getTemplateList")
    public R<List<WxOpenMaCodeTemplate>> getTemplateList(Integer templateType) {
        try {
            List<WxOpenMaCodeTemplate> templateList = wxOpenService.getWxOpenComponentService().getTemplateList(templateType);
            return R.ok(templateList);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @Description: 获取草稿箱列表
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/16 0016 17:34
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:info')")
    @ApiOperation("获取草稿箱列表")
    @GetMapping("/getTemplateDraftList")
    public R<List<WxOpenMaCodeTemplate>> getTemplateDraftList() {
        try {
            List<WxOpenMaCodeTemplate> templateDraftList = wxOpenService.getWxOpenComponentService().getTemplateDraftList();
            return R.ok(templateDraftList);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @descriptions 将草稿添加到模板库
     * @author DB
     * @date 2023/10/10 18:16
     * @param draftId 草稿id
     * @param templateType 模板类型
     * @return: com.cpop.core.base.R<java.lang.Void>
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:update')")
    @ApiOperation("将草稿添加到模板库")
    @PostMapping("/addToTemplate")
    public R<Void> addToTemplate(@RequestParam("draftId") Long draftId, @RequestParam("templateType") Integer templateType) {
        try {
            wxOpenService.getWxOpenComponentService().addToTemplate(draftId, templateType);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

    /**
     * @Description: 删除代码模板
     * @param templateId 模板id
     * @return R<Void>
     * @author DB
     * @Date: 2023/6/27 0027 14:28
     */
    @PreAuthorize("@aps.hasPermission('wxOpen:applet:remove')")
    @ApiOperation("删除代码模板")
    @DeleteMapping("/deleteTemplate/{templateId}")
    public R<Void> deleteTemplate(@PathVariable Integer templateId) {
        try {
            wxOpenService.getWxOpenComponentService().deleteTemplate(templateId);
        } catch (WxErrorException e) {
            throw new ServiceException(e.getMessage());
        }
        return R.ok();
    }

}
