package org.dromara.auth.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.auth.domain.vo.LoginVo;
import org.dromara.auth.form.WxPubLoginBody;
import org.dromara.auth.service.IAuthStrategy;
import org.dromara.auth.service.SysLoginService;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.enums.SmsType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.resource.api.RemoteWeiXinService;
import org.dromara.system.api.RemoteUserService;
import org.dromara.system.api.domain.vo.RemoteClientVo;
import org.dromara.system.api.model.LoginUser;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;

/**
 * 微信公众号认证策略
 *
 * @author Michelle.Chung
 */
@Slf4j
@Service("weixinpub" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class WxPubAuthStrategy implements IAuthStrategy {

    private final SysLoginService loginService;

    @DubboReference
    private RemoteUserService remoteUserService;

    @DubboReference
    private RemoteWeiXinService remoteWeiXinService;

    @Override
    public LoginVo login(String body, RemoteClientVo client) {
        WxPubLoginBody loginBody = JsonUtils.parseObject(body, WxPubLoginBody.class);
        ValidatorUtils.validate(loginBody);
        String tenantId = loginBody.getTenantId();
        if(ObjectUtil.isNull(loginBody.getId())){
            throw new ServiceException("登录参数缺失，请重新登录");
        }
        String resultKey = loginService.getWxPubLoginResultCacheKey(loginBody.getId());
        R<LoginVo> loginResultVo = RedisUtils.getCacheObject(resultKey);
        if (loginResultVo == null || loginResultVo.getData() == null || StringUtils.isBlank(loginResultVo.getData().getClientId())){
            throw new ServiceException("二维码已过期，请重新登录");
        }
        Long userId = Long.parseLong(loginResultVo.getData().getClientId());
        String curOpenId = remoteWeiXinService.getOpenId(Base64.decodeStr(loginBody.getDomain()),loginBody.getWxcode());
        if (StringUtils.isBlank(curOpenId)){
            loginResultVo.setMsg("授权失败，请重新登录");
            RedisUtils.setCacheObject(resultKey, loginResultVo, Duration.ofSeconds(RedisUtils.getTimeToLive(resultKey)));
            throw new ServiceException(loginResultVo.getMsg());
        }
        LoginUser loginUser = remoteUserService.getUserInfo(userId,tenantId);
        if(!curOpenId.equals(loginUser.getBindOpenId())){
            loginResultVo.setMsg("登录失败，绑定账户不正确");
            RedisUtils.setCacheObject(resultKey, loginResultVo, Duration.ofSeconds(RedisUtils.getTimeToLive(resultKey)));
            throw new ServiceException(loginResultVo.getMsg());
        }
        SmsType smsType = SmsType.getSmsTypeByRole(loginUser.getCurRoleId());
        String code = RedisUtils.getCacheObject(smsType.getCacheKey(loginUser.getUserId().toString()));
        if (StringUtils.isBlank(code) || !code.equals(loginBody.getCode())) {
            loginResultVo.setMsg("二维码已过期，请重新登录");
            RedisUtils.setCacheObject(resultKey, loginResultVo, Duration.ofSeconds(RedisUtils.getTimeToLive(resultKey)));
            loginService.recordLogininfor(tenantId, loginUser.getUsername(), Constants.LOGIN_FAIL, loginResultVo.getMsg());
            throw new BaseException("user",loginResultVo.getMsg());
        }
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        loginUser.setSydId(loginBody.getSydId());
        SaLoginModel model = new SaLoginModel();
        model.setDevice(client.getDeviceType());
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
        // 例如: 后台用户30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // 生成token
        LoginHelper.login(loginUser, model);

        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        long size = RedisUtils.getTimeToLive(resultKey);
//        RedisUtils.setCacheObject(resultKey, R.ok(loginVo), Duration.ofSeconds(RedisUtils.getTimeToLive(resultKey)));
        RedisUtils.setCacheObject(resultKey, R.ok(loginVo));
        return loginVo;
    }
}
