package com.ruoyi.ibs.app.service.impl;


import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.ibs.app.domain.*;
import com.ruoyi.ibs.app.mapper.QYWeiXinMapper;
import com.ruoyi.ibs.app.service.WeiXinService;
import com.ruoyi.ibs.app.util.Constants;
import com.ruoyi.ibs.app.util.JsonResult;
import com.ruoyi.ibs.app.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Service
public class WeiXinServiceImpl implements WeiXinService {


    @Autowired
    private QyWxpropertiex qyWeiXinProperties;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private QYWeiXinMapper qyWeiXinMapper;

    public static final String URL_KEY_VALUE = "jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s";

    private JsonResult<String> getAccessToken(boolean isRefresh, String redisKey, String url,String orgId) {
        String token = redisUtil.get( redisKey+orgId );
        if (isRefresh || StringUtils.isEmpty( token )) {
            //获取access_token
            AccessTokenDTO accessTokenDTO = restTemplate.getForObject( url, AccessTokenDTO.class );
            log.info( accessTokenDTO.toString() );
            if (accessTokenDTO.getErrCode() == null || accessTokenDTO.getErrCode() == 0) {
                token = accessTokenDTO.getAccessToken();
                //redis缓存access_token
                redisUtil.setStr( redisKey+orgId, token, accessTokenDTO.getExpiresIn() );
                return JsonResult.success( token );
            } else {
                log.error( "获取access_token失败," + accessTokenDTO );
                return JsonResult.error( Constants.FAILURE, "获取access_token失败" );
            }
        }
        return JsonResult.success( token );
    }

    /**
     * 构件 TicketVO
     *
     * @param ticket
     * @param appId
     *
     * @return
     */
    public JsonResult buildTicketVO(String ticket, String appId, String qyUrl,String agentId) {
        String nonceStr = UUID.randomUUID().toString().replace( "-", "" );
        long timestamp = System.currentTimeMillis() / 1000;
        String url = String.format( URL_KEY_VALUE, ticket, nonceStr, timestamp, qyUrl );
        String signature;
        try {
            signature = signature( url );
        } catch (NoSuchAlgorithmException e) {
            log.error( "签名算法异常" + e.getMessage() );
            return JsonResult.error( Constants.FAILURE, "签名算法异常" );
        }
        TicketVO ticketVO = new TicketVO();
        ticketVO.setAppId( appId );
        ticketVO.setNonceStr( nonceStr );
        ticketVO.setTimestamp( timestamp );
        ticketVO.setSignature( signature );
        ticketVO.setAgentId(agentId);
        return JsonResult.success( ticketVO );
    }

    /**
     * 对字符串进行sha1签名
     *
     * @param str
     *
     * @return
     *
     * @throws NoSuchAlgorithmException
     */
    private String signature(String str) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance( "SHA-1" );
        byte[] messageDigest = md.digest( str.getBytes() );
        BigInteger bigInteger = new BigInteger( 1, messageDigest );
        String hasText = bigInteger.toString( 16 );
        while (hasText.length() < 32) {
            hasText = "0" + hasText;
        }
        return hasText;
    }




    /**
     * 获取ticket
     *
     * @param token access_token
     * @param ticketKey ticket redis key
     * @param url ticket api url
     * @param accessKey 重新刷新获取access_token redis key
     * @param accessUrl 重新刷新获取access_token url
     *
     * @return
     */
    private JsonResult<String> getTicket(String token, String ticketKey, String url, String accessKey, String accessUrl,String orgId) {
        String ticketValue = redisUtil.get( ticketKey+orgId );
        if (StringUtils.isEmpty( ticketValue )) {
            TicketDTO ticketDTO = restTemplate.getForObject( url, TicketDTO.class, token );
            log.info( ticketDTO.toString() );
            if (ticketDTO.getErrCode() == 0) {
                ticketValue = ticketDTO.getTicket();
                //redis缓存ticket
                redisUtil.setStr( ticketKey+orgId, ticketValue, ticketDTO.getExpiresIn() );
            } else if (ticketDTO.getErrCode() == 40001) {
                // token过期重新获取
                JsonResult<String> accessTokenResult = getAccessToken( true, accessKey, accessUrl,orgId );
                ticketDTO = restTemplate.getForObject( url, TicketDTO.class, accessTokenResult.getData() );
                ticketValue = ticketDTO.getTicket();
                //redis缓存ticket
                redisUtil.setStr( ticketKey+orgId, ticketValue, ticketDTO.getExpiresIn() );
            } else {
                log.error( "获取ticket失败," + ticketDTO );
                return JsonResult.error( Constants.FAILURE, "获取ticket失败" );
            }
        }
        return JsonResult.success( ticketValue );
    }



    /**
     * 获取企业用户详细信息
     *
     * @param code 企业微信授权码，只能消费一次，并且五分钟内有效
     *
     * @return
     */
    @Override
    public JsonResult<QYUserDTO> getQYUser(String code,String appId,String secret,String orgId) {
        String url = String.format( qyWeiXinProperties.getAddress() + Constants.QY_WEI_XIN_ACCESS_TOKEN,appId, secret );
        JsonResult<String> accessTokenResult = getAccessToken( false, Constants.QY_WEI_XIN_ACCESS_TOKEN_KEY, url,orgId );
        if (!Constants.OK.equals( accessTokenResult.getCode() )) {
            return JsonResult.error( Constants.FAILURE, accessTokenResult.getMessage() );
        }
        String accessToken = accessTokenResult.getData();
        //获取访问用户身份
        QYUserDTO qyUserDTO = restTemplate.getForObject( qyWeiXinProperties.getAddress() + Constants.QY_WEI_XIN_USER_INFO, QYUserDTO.class, accessToken, code );
        if (( qyUserDTO != null ? qyUserDTO.getErrCode() : null ) == 40001) {
            //token过期,重新获取token
            JsonResult<String> newAccessTokenResult = getAccessToken( true, Constants.QY_WEI_XIN_ACCESS_TOKEN_KEY, url,orgId );
            accessToken = newAccessTokenResult.getData();
            qyUserDTO = restTemplate.getForObject( qyWeiXinProperties.getAddress() + Constants.QY_WEI_XIN_USER_INFO, QYUserDTO.class, accessToken, code );
        } else if (( qyUserDTO != null ? qyUserDTO.getErrCode() : null ) != 0) {
            log.error( "获取企业微信用户身份失败," + qyUserDTO );
            return JsonResult.error( Constants.FAILURE, "获取企业微信用户身份失败" );
        }
        if (qyUserDTO != null) {
            qyUserDTO.setAccessToken( accessToken );
        }
        log.info( qyUserDTO.toString() );
        return JsonResult.success( qyUserDTO );
    }

    @Override
    public JsonResult<QYUserDetailDTO> getQYUserDetail(QYUserDTO qyUserDTO) {
        Map<String, String> body = new HashMap<>();
        body.put( "user_ticket", qyUserDTO.getUserTicket() );
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType( MediaType.APPLICATION_JSON );
        //获取访问用户详细敏感信息
        ResponseEntity<QYUserDetailDTO> responseEntity = restTemplate
                .exchange( qyWeiXinProperties.getAddress() + Constants.QY_WEI_XIN_USER_DETAIL, HttpMethod.POST, new HttpEntity<>( body, httpHeaders ), QYUserDetailDTO.class,
                        qyUserDTO.getAccessToken() );
        QYUserDetailDTO qyUserDetailDTO = responseEntity.getBody();
        if (qyUserDetailDTO != null && qyUserDetailDTO.getErrCode() == 0) {
            return JsonResult.success( qyUserDetailDTO );
        }
        log.error( "获取企业微信用户敏感信息失败," + qyUserDetailDTO );
        return JsonResult.error( Constants.FAILURE, "获取企业微信用户敏感信息失败" );
    }

    @Override
    public JsonResult getTicketVo2(String qyUrl,String orgId) {
            QywxOrg qywxOrg = qyWeiXinMapper.getQywxOrg(orgId);
            String url = String.format( qyWeiXinProperties.getAddress() + Constants.QY_WEI_XIN_ACCESS_TOKEN, qywxOrg.getAppId(), qywxOrg.getSecret() );
            JsonResult<String> accessTokenResult = getAccessToken( false, Constants.WEI_XIN_ACCESS_TOKEN_KEY, url,orgId );
            if (!Constants.OK.equals( accessTokenResult.getCode() )) {
                return accessTokenResult;
            }
            JsonResult<String> ticketValueResult =
                    getTicket( accessTokenResult.getData(), Constants.QY_WEI_XIN_TICKET_KEY, qyWeiXinProperties.getAddress() + Constants.QY_WEI_XIN_JSAPI_TICKET,
                            Constants.WEI_XIN_ACCESS_TOKEN_KEY, url,orgId );
            if (!Constants.OK.equals( ticketValueResult.getCode() )) {
                return ticketValueResult;
            }
            System.out.println("------------------------------------");
            System.out.println(ticketValueResult);
            System.out.println("------------------------------------");
            System.out.println(ticketValueResult.getData());
            System.out.println("------------------------------------");
            System.out.println(qyUrl);
            System.out.println("------------------------------------");
            return buildTicketVO( ticketValueResult.getData(), qywxOrg.getAppId(), qyUrl ,qywxOrg.getAgentId());
        }

    public AjaxResult getIp(String orgId) {
        QywxOrg qywxOrg = qyWeiXinMapper.getQywxOrg(orgId);
        String url = String.format( qyWeiXinProperties.getAddress() + Constants.QY_WEI_XIN_ACCESS_TOKEN, qywxOrg.getAppId(), qywxOrg.getSecret() );
        JsonResult<String> accessTokenResult = getAccessToken( false, Constants.WEI_XIN_ACCESS_TOKEN_KEY, url,orgId );
        IpDomain ipDomain = restTemplate.getForObject(  qyWeiXinProperties.getAddress()+Constants.QY_WEI_XIN_API_IP,IpDomain.class,accessTokenResult.getData());
        return AjaxResult.success(ipDomain);
    }



}
