package com.sunyard.chsm.service;

import com.sunyard.chsm.auth.AppUser;
import com.sunyard.chsm.constant.SecurityConstant;
import com.sunyard.chsm.enums.EnableStatus;
import com.sunyard.chsm.mapper.AppServiceMapper;
import com.sunyard.chsm.mapper.ApplicationMapper;
import com.sunyard.chsm.model.entity.AppService;
import com.sunyard.chsm.model.entity.Application;
import com.sunyard.chsm.param.AppTokenReq;
import com.sunyard.chsm.param.AppTokenResp;
import com.sunyard.chsm.utils.CodecUtils;
import com.sunyard.chsm.utils.gm.BCSM3Utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author liulu
 * @since 2024/12/5
 */
@Slf4j
@Service
public class AppLoginService {

    /**
     * token 过期时间, 分钟
     */
    @Value("${chsm.token.expireTime:720}")
    private Integer tokenExpireTime;
    @Resource
    private ApplicationMapper applicationMapper;
    @Resource
    private AppServiceMapper appServiceMapper;

    public AppTokenResp getAppToken(AppTokenReq req) {
        Long random = req.getRandom();
        long now = System.currentTimeMillis();
        Assert.isTrue(now - random <= 5 * 60 * 1000, "请求已超时");
        String appKey = req.getAppKey();
        Application application = applicationMapper.selectByAppKey(appKey);
        Assert.isTrue(EnableStatus.ENABLED.getCode().equals(application.getStatus()), "此应用已停用");
        String data = appKey + random + application.getAppSecret();
        byte[] hmac = BCSM3Utils.hmac(application.getAppSecret().getBytes(), data.getBytes());
        String serverHmac = CodecUtils.encodeHex(hmac);
        if (!Objects.equals(req.getHmac(), serverHmac)) {
            log.warn("appKey: {}, req hmac: {}, server hmac: {}", appKey, req.getHmac(), serverHmac);
            throw new IllegalArgumentException("应用认证失败");
        }
        AppTokenResp resp = new AppTokenResp();
        resp.setToken(genToken(application));
        return resp;
    }

    private String genToken(Application app) {
        List<AppService> appServices = appServiceMapper.selectByAppIds(Collections.singletonList(app.getId()));
        List<Long> serviceIds = appServices.stream().map(AppService::getServiceId).collect(Collectors.toList());
        Map<String, Object> claims = new HashMap<>();
        claims.put("appId", app.getId());
        claims.put("name", app.getName());
        claims.put("serviceIds", serviceIds);
        Date now = new Date();
        return Jwts.builder()
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(new Date(now.getTime() + tokenExpireTime * 60 * 1000))
                .setId(UUID.randomUUID().toString())
                .signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY)
                .compact();
    }

    public AppTokenResp getAppTokenForTest(@Valid AppTokenReq req) {

//        Long random = req.getRandom();
//        long now = System.currentTimeMillis();
//        Assert.isTrue(now - random <= 5 * 60 * 1000, "请求已超时");
        Application application = applicationMapper.selectByAppKey(req.getAppKey());
        Assert.isTrue(EnableStatus.ENABLED.getCode().equals(application.getStatus()), "此应用已停用");
        if (!Objects.equals(req.getHmac(), application.getAppSecret())) {
            log.warn("appKey: {}, req hmac: {},", req.getAppKey(), req.getHmac());
            throw new IllegalArgumentException("应用认证失败");
        }
        AppTokenResp resp = new AppTokenResp();
        resp.setToken(genToken(application));
        return resp;
    }


    public AppUser verifyToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(SecurityConstant.JWT_SIGN_KEY)
                .parseClaimsJws(token)
                .getBody();

        AppUser user = new AppUser();
        user.setTokenId(claims.get("jti", String.class));
        user.setAppId(claims.get("appId", Long.class));
        user.setName(claims.get("name", String.class));
        user.setServiceIds(claims.get("serviceIds", List.class));
        return user;
    }


}
