package com.iformall.service.wechat;

import com.iformall.common.FmHttpClientBuilder;
import com.iformall.domain.po.WxAuthorizerInfo;
import com.iformall.domain.po.WxComponentVerifyTicket;
import com.iformall.mapper.WxAuthorizerInfoMapper;
import com.iformall.mapper.WxComponentVerifyTicketMapper;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.util.Pool;

import java.util.Date;

public class FmOpenInRedisDBConfigStorage extends WxOpenInMemoryConfigStorage {
    private final static String COMPONENT_VERIFY_TICKET_KEY = "wechat_component_verify_ticket:";
    private final static String COMPONENT_ACCESS_TOKEN_KEY = "wechat_component_access_token:";

    private final static String AUTHORIZER_REFRESH_TOKEN_KEY = "wechat_authorizer_refresh_token:";
    private final static String AUTHORIZER_ACCESS_TOKEN_KEY = "wechat_authorizer_access_token:";
    private final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket:";
    private final static String CARD_API_TICKET_KEY = "wechat_card_api_ticket:";

    protected final Pool<Jedis> jedisPool;
    private WxComponentVerifyTicketMapper wxComponentVerifyTicketMapper;
    private WxAuthorizerInfoMapper wxAuthorizerInfoMapper;


    /**
     * redis 存储的 key 的前缀，可为空
     */
    private String keyPrefix;
    private String componentVerifyTicketKey;
    private String componentAccessTokenKey;
    private String authorizerRefreshTokenKey;
    private String authorizerAccessTokenKey;
    private String jsapiTicketKey;
    private String cardApiTicket;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());


    public FmOpenInRedisDBConfigStorage(Pool<Jedis> jedisPool) {
        this.jedisPool = jedisPool;
    }

    public FmOpenInRedisDBConfigStorage(Pool<Jedis> jedisPool, String keyPrefix) {
        this.jedisPool = jedisPool;
        this.keyPrefix = keyPrefix;
    }

    public FmOpenInRedisDBConfigStorage(JedisPool jedisPool, WxComponentVerifyTicketMapper componentVerifyTicketMapper, WxAuthorizerInfoMapper authorizerInfoMapper) {
        this.jedisPool = jedisPool;
        this.wxComponentVerifyTicketMapper = componentVerifyTicketMapper;
        this.wxAuthorizerInfoMapper = authorizerInfoMapper;
    }

    @Override
    public void setComponentAppId(String componentAppId) {
        super.setComponentAppId(componentAppId);
        String prefix = StringUtils.isBlank(keyPrefix) ? "" :
                (StringUtils.endsWith(keyPrefix, ":") ? keyPrefix : (keyPrefix + ":"));
        componentVerifyTicketKey = prefix + COMPONENT_VERIFY_TICKET_KEY.concat(componentAppId);
        componentAccessTokenKey = prefix + COMPONENT_ACCESS_TOKEN_KEY.concat(componentAppId);
        authorizerRefreshTokenKey = prefix + AUTHORIZER_REFRESH_TOKEN_KEY.concat(componentAppId);
        authorizerAccessTokenKey = prefix + AUTHORIZER_ACCESS_TOKEN_KEY.concat(componentAppId);
        this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(componentAppId);
        this.cardApiTicket = CARD_API_TICKET_KEY.concat(componentAppId);
    }

    @Override
    public String getComponentVerifyTicket() {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.get(this.componentVerifyTicketKey);
        }
    }

    @Override
    public void setComponentVerifyTicket(String componentVerifyTicket) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.set(this.componentVerifyTicketKey, componentVerifyTicket);
        }
    }

    @Override
    public String getComponentAccessToken() {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.get(this.componentAccessTokenKey);
        }
    }

    @Override
    public boolean isComponentAccessTokenExpired() {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.ttl(this.componentAccessTokenKey) < 2;
        }
    }

    @Override
    public void expireComponentAccessToken(){
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.expire(this.componentAccessTokenKey, 0);
        }
    }

    @Override
    public void updateComponentAccessTokent(String componentAccessToken, int expiresInSeconds) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.setex(this.componentAccessTokenKey, expiresInSeconds - 200, componentAccessToken);

            WxComponentVerifyTicket ticket = new WxComponentVerifyTicket();
            ticket.setComponentAppid(getComponentAppId());
            ticket.setAccessToken(componentAccessToken);
            ticket.setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + (expiresInSeconds * 1000))));
            int num = wxComponentVerifyTicketMapper.updateAccessToken(ticket);
            logger.info("updateComponentVerifyTicketAccessToken rows: " + num);
        }
    }

    private String getKey(String prefix, String appId) {
        return prefix.endsWith(":") ? prefix.concat(appId) : prefix.concat(":").concat(appId);
    }

    @Override
    public String getAuthorizerRefreshToken(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.get(this.getKey(this.authorizerRefreshTokenKey, appId));
        }
    }

    @Override
    public void setAuthorizerRefreshToken(String appId, String authorizerRefreshToken) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.set(this.getKey(this.authorizerRefreshTokenKey, appId), authorizerRefreshToken);

            WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
            authorizerInfo.setAuthorizerAppid(appId);
            authorizerInfo.setRefreshToken(authorizerRefreshToken);
            int num = wxAuthorizerInfoMapper.updateRefreshToken(authorizerInfo);
            logger.info("setAuthorizerRefreshToken " + appId + " rows: " + num);
        }
    }

    @Override
    public String getAuthorizerAccessToken(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.get(this.getKey(this.authorizerAccessTokenKey, appId));
        }
    }

    @Override
    public boolean isAuthorizerAccessTokenExpired(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.ttl(this.getKey(this.authorizerAccessTokenKey, appId)) < 2;
        }
    }

    @Override
    public void expireAuthorizerAccessToken(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.expire(this.getKey(this.authorizerAccessTokenKey, appId), 0);
        }
    }

    @Override
    public void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) {
        this.updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(), authorizerAccessToken.getExpiresIn());

        WxAuthorizerInfo authorizerInfo = new WxAuthorizerInfo();
        authorizerInfo.setAuthorizerAppid(appId);
        authorizerInfo.setAccessToken(authorizerAccessToken.getAuthorizerAccessToken());
        authorizerInfo.setAccessTokenExpire(new Date(Long.valueOf(System.currentTimeMillis() + (authorizerAccessToken.getExpiresIn() * 1000))));
        int num = wxAuthorizerInfoMapper.updateAccessToken(authorizerInfo);
        logger.info("updateAuthorizerAccessToken " + appId + " rows: " + num);
    }

    @Override
    public void updateAuthorizerAccessToken(String appId, String authorizerAccessToken, int expiresInSeconds) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.setex(this.getKey(this.authorizerAccessTokenKey, appId), expiresInSeconds - 200, authorizerAccessToken);
        }
    }

    @Override
    public String getJsapiTicket(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.get(this.getKey(this.jsapiTicketKey, appId));
        }
    }

    @Override
    public boolean isJsapiTicketExpired(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.ttl(this.getKey(this.jsapiTicketKey, appId)) < 2;
        }
    }

    @Override
    public void expireJsapiTicket(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.expire(this.getKey(this.jsapiTicketKey, appId), 0);
        }
    }

    @Override
    public void updateJsapiTicket(String appId, String jsapiTicket, int expiresInSeconds) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.setex(this.getKey(this.jsapiTicketKey, appId), expiresInSeconds - 200, jsapiTicket);
        }

        // TODO update to DB
    }

    @Override
    public String getCardApiTicket(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.get(this.getKey(this.cardApiTicket, appId));
        }
    }

    @Override
    public boolean isCardApiTicketExpired(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.ttl(this.getKey(this.cardApiTicket, appId)) < 2;
        }
    }

    @Override
    public void expireCardApiTicket(String appId) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.expire(this.getKey(this.cardApiTicket, appId), 0);
        }
    }

    @Override
    public void updateCardApiTicket(String appId, String cardApiTicket, int expiresInSeconds) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            jedis.setex(this.getKey(this.cardApiTicket, appId), expiresInSeconds - 200, cardApiTicket);
        }

        // TODO update to DB
    }

    @Override
    public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
        return FmHttpClientBuilder.get();
    }
}
