package com.fs.mp.controller;

import com.alibaba.fastjson.JSON;
import com.fs.common.core.domain.R;
import com.fs.mp.config.MpSilentAuthProperties;
import com.fs.mp.param.MpSilentResolveParam;
import com.fs.mp.param.MpSilentUserInfoParam;
import com.fs.mp.service.MpSilentAuthService;
import com.fs.mp.vo.MpSilentAuthResult;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 服务号 H5 静默授权（snsapi_base）
 * <p>本 Controller 全部接口匿名访问，不做登录 / Token 权限校验。</p>
 */
@Slf4j
@RestController
@RequestMapping("/mp/silent")
public class MpSilentAuthController {

    @Autowired
    private WxMpService wxMpService;

    @Autowired
    private MpSilentAuthService mpSilentAuthService;

    @Autowired
    private MpSilentAuthProperties silentAuthProperties;

    /**
     * 生成静默授权 URL
     */
    @GetMapping("/authUrl")
    public R authUrl(@RequestParam String redirectUri,
                     @RequestParam(required = false) String appId,
                     @RequestParam(required = false) String state) {
        log.info("[authUrl] 收到请求 redirectUri={}, appId={}, state={}", redirectUri, appId, state);
        if (StringUtils.isBlank(redirectUri)) {
            log.warn("[authUrl] redirectUri 为空，拒绝");
            return R.error("redirectUri 不能为空");
        }
        if (!isAllowedRedirectUri(redirectUri)) {
            log.warn("[authUrl] redirectUri 不在白名单 redirectUri={}, allowedHosts={}",
                    redirectUri, silentAuthProperties.getAllowedRedirectHosts());
            return R.error("redirectUri 域名不在白名单中");
        }
        try {
            WxMpService service = switchMp(appId);
            String oauthUrl = service.getOAuth2Service().buildAuthorizationUrl(
                    redirectUri,
                    WxConsts.OAuth2Scope.SNSAPI_BASE,
                    StringUtils.defaultString(state, "")
            );
            log.info("[authUrl] 生成授权链接成功 scope=snsapi_base, oauthUrl={}", oauthUrl);
            Map<String, Object> data = new HashMap<>(2);
            data.put("oauthUrl", oauthUrl);
            return R.ok(data);
        } catch (IllegalArgumentException e) {
            log.warn("[authUrl] 参数错误: {}", e.getMessage());
            return R.error(e.getMessage());
        } catch (Exception e) {
            log.error("[authUrl] 生成静默授权链接失败 redirectUri={}, appId={}", redirectUri, appId, e);
            return R.error("生成授权链接失败");
        }
    }

    /**
     * code 换 openId，再走业务扩展点返回最终 url
     */
    @PostMapping("/resolve")
    public R resolve(@RequestBody MpSilentResolveParam param) {
        log.info("[resolve] 收到请求 code={}, appId={}, state={}",
                maskCode(param == null ? null : param.getCode()),
                param == null ? null : param.getAppId(),
                param == null ? null : param.getState());
        if (param == null || StringUtils.isBlank(param.getCode())) {
            log.warn("[resolve] code 为空，拒绝");
            return R.error("code 不能为空");
        }
        try {
            WxMpService service = switchMp(param.getAppId());
            log.info("[resolve] 开始用 code 换取 accessToken, appId={}", param.getAppId());
            WxOAuth2AccessToken accessToken = service.getOAuth2Service().getAccessToken(param.getCode());
            log.info("获取公众号用户信息：{}", JSON.toJSONString(accessToken));
            String openId = accessToken.getOpenId();
            log.info("[resolve] 换票成功 openId={}, unionId={}, expiresIn={}",
                    openId,
                    accessToken.getUnionId(),
                    accessToken.getExpiresIn());
            if (StringUtils.isBlank(openId)) {
                log.warn("[resolve] accessToken 中 openId 为空");
                return R.error("未获取到 openId");
            }
            log.info("[resolve] 进入业务扩展点 MpSilentAuthService.resolve");
            MpSilentAuthResult biz = mpSilentAuthService.resolve(openId, param.getState());
            return R.ok(buildBizData(openId, biz));
        } catch (WxErrorException e) {
            log.error("[resolve] 微信接口失败 code={}, errCode={}, errMsg={}",
                    maskCode(param.getCode()),
                    e.getError() != null ? e.getError().getErrorCode() : null,
                    e.getError() != null ? e.getError().getErrorMsg() : e.getMessage(),
                    e);
            return R.error("微信授权失败：" + e.getError().getErrorMsg());
        } catch (IllegalArgumentException e) {
            log.warn("[resolve] 参数错误: {}", e.getMessage());
            return R.error(e.getMessage());
        } catch (Exception e) {
            log.error("[resolve] 处理失败 appId={}, state={}", param.getAppId(), param.getState(), e);
            return R.error("处理失败");
        }
    }

    /**
     * 已有 openId（本地缓存、二次进入）时，直接查拉黑/加销售等信息，无需再授权
     */
    @PostMapping("/userInfo")
    public R userInfo(@RequestBody MpSilentUserInfoParam param) {
        log.info("[userInfo] 收到请求 openId={}, state={}",
                param == null ? null : param.getOpenId(),
                param == null ? null : param.getState());
        if (param == null || StringUtils.isBlank(param.getOpenId())) {
            return R.error("openId 不能为空");
        }
        try {
            MpSilentAuthResult biz = mpSilentAuthService.resolve(param.getOpenId(), param.getState());
            return R.ok(buildBizData(param.getOpenId(), biz));
        } catch (Exception e) {
            log.error("[userInfo] 处理失败 openId={}", param.getOpenId(), e);
            return R.error("处理失败");
        }
    }

    private Map<String, Object> buildBizData(String openId, MpSilentAuthResult biz) {
        log.info("[biz] openId={}, userFound={}, blacklisted={}, addSalesCount={}, tip={}, url={}",
                openId,
                biz.isUserFound(),
                biz.isBlacklisted(),
                biz.getAddSalesCount(),
                biz.getTip(),
                biz.getUrl());
        Map<String, Object> data = new HashMap<>(8);
        data.put("openId", openId);
        data.put("url", biz.getUrl() == null ? "" : biz.getUrl());
        data.put("userFound", biz.isUserFound());
        data.put("userId", biz.getUserId());
        data.put("blacklisted", biz.isBlacklisted());
        data.put("manageRemark", biz.getManageRemark());
        data.put("addSalesCount", biz.getAddSalesCount());
        data.put("tip", biz.getTip());
        return data;
    }

    private WxMpService switchMp(String appId) {
        if (StringUtils.isNotBlank(appId)) {
            log.info("[switchMp] 切换公众号 appId={}", appId);
            if (!wxMpService.switchover(appId)) {
                log.warn("[switchMp] 未找到公众号配置 appId={}", appId);
                throw new IllegalArgumentException("未找到公众号配置 appId=" + appId);
            }
            WxMpService switched = wxMpService.switchoverTo(appId);
            log.info("[switchMp] 切换成功 appId={}", appId);
            return switched;
        }
        log.info("[switchMp] 未传 appId，使用默认公众号配置");
        return wxMpService;
    }

    private boolean isAllowedRedirectUri(String redirectUri) {
        List<String> hosts = silentAuthProperties.getAllowedRedirectHosts();
        if (hosts == null || hosts.isEmpty()) {
            log.info("[authUrl] 白名单为空，跳过 redirectUri 校验 redirectUri={}", redirectUri);
            return true;
        }
        try {
            URI uri = URI.create(redirectUri);
            String host = uri.getHost();
            if (StringUtils.isBlank(host)) {
                log.warn("[authUrl] 无法解析 redirectUri host, redirectUri={}", redirectUri);
                return false;
            }
            for (String allowed : hosts) {
                if (host.equalsIgnoreCase(allowed) || host.endsWith("." + allowed)) {
                    log.info("[authUrl] redirectUri 白名单校验通过 host={}, allowed={}", host, allowed);
                    return true;
                }
            }
            log.warn("[authUrl] redirectUri 白名单校验失败 host={}, allowedHosts={}", host, hosts);
            return false;
        } catch (Exception e) {
            log.warn("[authUrl] 解析 redirectUri 异常 redirectUri={}", redirectUri, e);
            return false;
        }
    }

    /** code 较长，日志里只保留前后几位便于排查 */
    private String maskCode(String code) {
        if (StringUtils.isBlank(code)) {
            return code;
        }
        if (code.length() <= 8) {
            return "****";
        }
        return code.substring(0, 4) + "****" + code.substring(code.length() - 4);
    }
}
