package com.sonic.frog.client.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwk.InvalidPublicKeyException;
import com.auth0.jwk.Jwk;
import com.sonic.common.AppRuntime;
import com.sonic.common.auth.domains.AppClientEnum;
import com.sonic.common.rpc.RequestParams;
import com.sonic.common.rpc.RpcClient;
import com.sonic.frog.client.AppleIdLoginValidateOauth2Client;
import com.sonic.frog.domain.bo.ThirdAuthBo;
import com.sonic.frog.utils.AppleThirdUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PublicKey;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author code
 */
@Slf4j
@Service
public class AppleIdLoginValidateOauth2ClientImpl implements AppleIdLoginValidateOauth2Client {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RpcClient rpcClient;
    @Autowired
    private AppRuntime appRuntime;

    private static final int APPLE_ID_PUBLIC_KEY_EXPIRE = 24;

    @Value("${thirdLogin.apple.clientId}")
    private String client_id;
    @Value("${thirdLogin.apple.redirectUri}")
    private String redirect_uri;
    @Value("${thirdLogin.apple.aud}")
    private String AUD;

    @Override
    public boolean isValid(String accessToken, AppClientEnum appClient) {
        if (AppClientEnum.IOS.equals(appClient)) {
            //校验基本信息:nonce,iss,aud,exp
            CusJws cusJws = this.getJws(accessToken);
            if (cusJws == null) {
                return false;
            }
            String aud = cusJws.getJwsPayload().getAud();
            if (!AUD.equalsIgnoreCase(aud)) {
                return false;
            }
            //校验签名
            PublicKey publicKey = this.getAppleIdPublicKey(cusJws.getJwsHeader().getKid());
            return this.verifySignature2(publicKey, accessToken, aud, cusJws.getJwsPayload().getSub());
        } else if (AppClientEnum.WEB.equals(appClient)) {
            String json = AppleThirdUtils.verify(accessToken);
            if (StringUtils.isEmpty(json)) {
                return false;
            }
            JSONObject jsonObject = JSON.parseObject(json);
            //判断应用ID必须为配置中的
            if(!AppleThirdUtils.APPLICATION_ID.equals(jsonObject.getString("aud"))) {
                return false;
            }
            return !StringUtils.isEmpty(jsonObject.getString("auth_time"));
        }
        return false;
    }

    @Override
    public ThirdAuthBo parseIdByIdToken(String idToken) {
        //参考文档地址：https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple
        ThirdAuthBo thirdAuthBo = new ThirdAuthBo();
        String json = AppleThirdUtils.verify(idToken);
        log.info("===> parseIdByIdToken result : {}", json);
        if(StringUtils.isEmpty(json)) {
            return thirdAuthBo;
        }
        JSONObject jsonObject = JSON.parseObject(json);
        String thirdId = jsonObject.getString("sub");
        thirdAuthBo.setThirdId(thirdId);
        String emailVerified = jsonObject.getString("email_verified");
        if(Boolean.TRUE.toString().equals(emailVerified)) {
            String email = jsonObject.getString("email");
            thirdAuthBo.setEmail(email);
        }
        return thirdAuthBo;
    }


    /**
     * 参数验签
     * @param key
     * @param jwt
     * @param audience
     * @param subject
     * @return
     */
    public boolean verifySignature2(PublicKey key, String jwt, String audience, String subject) {
        JwtParser jwtParser = Jwts.parser().setSigningKey(key);
        jwtParser.requireIssuer("https://appleid.apple.com");
        jwtParser.requireAudience(audience);
        jwtParser.requireSubject(subject);
        try {
            Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
            return claim != null && claim.getBody().containsKey("auth_time");
        } catch (Exception e) {
            log.error("===> verifySignature2 error : ", e);
            return false;
        }
    }

    /**
     * publicKey会本地缓存1天
     * @param kid
     * @return
     */
    private PublicKey getAppleIdPublicKey(String kid) {
        String redisKey = appRuntime.buildPrefixKey("appleid", "publickey");
        String publicKeyStr = stringRedisTemplate.opsForValue().get(redisKey);
        if (publicKeyStr == null) {
            publicKeyStr = this.getAppleIdPublicKeyFromRemote();
            if (publicKeyStr == null) {
                return null;
            }
            try {
                PublicKey publicKey = this.publicKeyAdapter(publicKeyStr, kid);
                stringRedisTemplate.opsForValue().set(redisKey, publicKeyStr, APPLE_ID_PUBLIC_KEY_EXPIRE, TimeUnit.HOURS);
                return publicKey;
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        return this.publicKeyAdapter(publicKeyStr, kid);
    }

    /**
     * 将appleServer返回的publicKey转换成PublicKey对象
     *
     * @param publicKeyStr
     * @return
     */
    private PublicKey publicKeyAdapter(String publicKeyStr, String kid) {
        if (!StringUtils.hasText(publicKeyStr)) {
            return null;
        }
        Map maps = (Map) JSON.parse(publicKeyStr);
        List<Map> keys = (List<Map>) maps.get("keys");
        Map o = null;
        for (Map key : keys) {
            if (kid.equals(key.get("kid"))) {
                o = key;
                break;
            }
        }
        Jwk jwa = Jwk.fromValues(o);
        try {
            PublicKey publicKey = jwa.getPublicKey();
            return publicKey;
        } catch (InvalidPublicKeyException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 从appleServer获取publicKey
     *
     * @return
     */
    private String getAppleIdPublicKeyFromRemote() {
        return rpcClient.get("https://appleid.apple.com/auth/keys",
                (bytes, stringListMap) -> bytes == null ? null : new String(bytes),
                new RequestParams.FormParams(null, false, null, 1000L));
    }

    private CusJws getJws(String identityToken) {
        String[] arrToken = identityToken.split("\\.");
        if (arrToken.length != 3) {
            return null;
        }
        Base64.Decoder decoder = Base64.getDecoder();
        JwsHeader jwsHeader = JSON.parseObject(new String(decoder.decode(arrToken[0])), JwsHeader.class);
        JwsPayload jwsPayload = JSON.parseObject(new String(decoder.decode(arrToken[1])), JwsPayload.class);
        return new CusJws(jwsHeader, jwsPayload, arrToken[2]);
    }

}
