package org.dromara.mp.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.enums.TicketType;
import me.chanjar.weixin.mp.api.WxMpService;
import org.apache.commons.codec.digest.DigestUtils;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.mp.annotation.SwitchoverCheck;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

/**
 * 微信公众号OAuth2控制器
 *
 * @author pengles
 */
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/wx/mp/oauth2/{appid}")
public class WxMpOAuth2Controller extends BaseController {

    private final WxMpService wxMpService;

    /**
     * 构建授权URL
     *
     * @param appid       公众号appid
     * @param redirectUrl 重定向URL
     * @param scope       授权作用域（snsapi_base静默授权，snsapi_userinfo用户信息授权）
     * @param state       防CSRF攻击的随机字符串
     * @return 授权URL
     */
    @GetMapping("/buildAuthorizationUrl")
    @SwitchoverCheck
    public R<String> buildAuthorizationUrl(@PathVariable String appid,
                                           @RequestParam String redirectUrl,
                                           @RequestParam(defaultValue = "snsapi_userinfo") String scope,
                                           @RequestParam String state) {
        try {
            String url = wxMpService.getOAuth2Service().buildAuthorizationUrl(redirectUrl, scope, state);
            return R.ok(url);
        } catch (Exception e) {
            log.error("构建授权URL失败: {}", e.getMessage(), e);
            return R.fail("构建授权URL失败: " + e.getMessage());
        }
    }

    /**
     * 获取访问令牌
     *
     * @param appid 公众号appid
     * @param code  授权码
     * @return 访问令牌信息
     */
    @GetMapping("/getAccessToken")
    @SwitchoverCheck
    public R<WxOAuth2AccessToken> getAccessToken(@PathVariable String appid,
                                                 @RequestParam String code) {
        try {
            WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code);
            return R.ok(accessToken);
        } catch (Exception e) {
            log.error("获取访问令牌失败: {}", e.getMessage(), e);
            return R.fail("获取访问令牌失败: " + e.getMessage());
        }
    }

    /**
     * 刷新访问令牌
     *
     * @param appid        公众号appid
     * @param refreshToken 刷新令牌
     * @return 新的访问令牌信息
     */
    @GetMapping("/refreshAccessToken")
    @SwitchoverCheck
    public R<WxOAuth2AccessToken> refreshAccessToken(@PathVariable String appid,
                                                     @RequestParam String refreshToken) {
        try {
            WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().refreshAccessToken(refreshToken);
            return R.ok(accessToken);
        } catch (Exception e) {
            log.error("刷新访问令牌失败: {}", e.getMessage(), e);
            return R.fail("刷新访问令牌失败: " + e.getMessage());
        }
    }

    /**
     * 验证访问令牌
     *
     * @param appid       公众号appid
     * @param accessToken 访问令牌
     * @param openId      用户openid
     * @return 验证结果
     */
    @PostMapping("/validateAccessToken")
    @SwitchoverCheck
    public R<Boolean> validateAccessToken(@PathVariable String appid,
                                          @RequestParam String accessToken,
                                          @RequestParam String openId) {
        try {
            WxOAuth2AccessToken wxOAuth2AccessToken = new WxOAuth2AccessToken();
            wxOAuth2AccessToken.setAccessToken(accessToken);
            wxOAuth2AccessToken.setOpenId(openId);
            boolean isValid = wxMpService.getOAuth2Service().validateAccessToken(wxOAuth2AccessToken);
            return R.ok(isValid);
        } catch (Exception e) {
            log.error("验证访问令牌失败: {}", e.getMessage(), e);
            return R.fail("验证访问令牌失败: " + e.getMessage());
        }
    }

    /**
     * 获取JSAPI签名
     *
     * @param appid 公众号appid
     * @param url   当前网页URL
     * @return JSAPI签名信息
     */
    @GetMapping("/getSignature")
    @SwitchoverCheck
    public R<Object> getSignature(@PathVariable String appid,
                                  @RequestParam String url) {
        try {
            String jsapiTicket = wxMpService.getTicket(TicketType.JSAPI);
            String nonceStr = UUID.randomUUID().toString();
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
            String signature = DigestUtils.sha1Hex(
                    "jsapi_ticket=" + jsapiTicket +
                            "&noncestr=" + nonceStr +
                            "&timestamp=" + timestamp +
                            "&url=" + url
            );

            WxSignatureVo signatureVo = new WxSignatureVo();
            signatureVo.setAppId(appid);
            signatureVo.setNonceStr(nonceStr);
            signatureVo.setTimestamp(timestamp);
            signatureVo.setSignature(signature);
            signatureVo.setUrl(url);

            return R.ok(signatureVo);
        } catch (Exception e) {
            log.error("获取JSAPI签名失败: {}", e.getMessage(), e);
            return R.fail("获取JSAPI签名失败: " + e.getMessage());
        }
    }

    /**
     * 获取用户信息
     *
     * @param appid       公众号appid
     * @param accessToken 访问令牌
     * @param openId      用户openid
     * @param lang        语言
     * @return 用户信息
     */
    @GetMapping("/getUserInfo")
    @SwitchoverCheck
    public R<Object> getUserInfo(@PathVariable String appid,
                                 @RequestParam String accessToken,
                                 @RequestParam String openId,
                                 @RequestParam(defaultValue = "zh_CN") String lang) {
        try {
            WxOAuth2AccessToken token = new WxOAuth2AccessToken();
            token.setAccessToken(accessToken);
            token.setOpenId(openId);

            Object userInfo = wxMpService.getOAuth2Service().getUserInfo(token, lang);
            return R.ok(userInfo);
        } catch (Exception e) {
            log.error("获取用户信息失败: {}", e.getMessage(), e);
            return R.fail("获取用户信息失败: " + e.getMessage());
        }
    }

    /**
     * 签名VO类
     */
    public static class WxSignatureVo {
        private String appId;
        private String nonceStr;
        private String timestamp;
        private String signature;
        private String url;

        // Getters and Setters
        public String getAppId() {
            return appId;
        }

        public void setAppId(String appId) {
            this.appId = appId;
        }

        public String getNonceStr() {
            return nonceStr;
        }

        public void setNonceStr(String nonceStr) {
            this.nonceStr = nonceStr;
        }

        public String getTimestamp() {
            return timestamp;
        }

        public void setTimestamp(String timestamp) {
            this.timestamp = timestamp;
        }

        public String getSignature() {
            return signature;
        }

        public void setSignature(String signature) {
            this.signature = signature;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }
}
