package link.at17.mid.tushare.data.crawler.tushare;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;

import com.alibaba.fastjson2.JSONObject;

import link.at17.mid.tushare.data.crawler.RetryAndDelay;
import link.at17.mid.tushare.system.util.SpringContextHolder;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

/**
 * 用来处理请求、请求返回结果，并返回是否执行成功
 * @author Barry
 *
 */
@Slf4j
@Data
@Accessors(chain=true)
public class TushareResponseCallable implements Callable<TushareCrawlerResult> {
    
    private TushareRequestBody request;
    private RetryAndDelay retryAndDelay = new RetryAndDelay();
    private Function<List<JSONObject>, Boolean> function;
    private static final Pattern MIN_LIMIT_PATTERN = Pattern.compile("分钟.*?(\\d+)次");
    private static final Pattern HOUR_LIMIT_PATTERN = Pattern.compile("小时.*?(\\d+)次");

    /**
     * 使用 TushareRequestBody 对象和对应 Function 初始化
     * @param request
     * @param consumer
     */
    public TushareResponseCallable(TushareRequestBody request, Function<List<JSONObject>, Boolean> function) {
        this.request = request;
        this.function = function;
    }
    
    @Override
    public TushareCrawlerResult call() throws Exception {
        if (!request.hasFingerprint()) {
            return TushareCrawlerResult.builder()
                .request(request)
                .isSuccess(false)
                .isFatal(true)
                .msg("请求指纹缺失，请检查")
                .build();
        }
        RedissonClient client = SpringContextHolder.getBean(RedissonClient.class);
        RBucket<Object> bucket = client.getBucket(request.getFingerprint());
        int retryTime = retryAndDelay.getRetryTime();
        while (retryTime-- > 0) {
            if (bucket.isExists()) {
                return TushareCrawlerResult.builder()
                        .request(request)
                        .isSuccess(false)
                        .isFatal(true)
                        .msg("Request cancelled for existing fingerprint bucket.")
                        .build();
            }
            try {
                List<JSONObject> list = TushareClient.queryList(request);
                if (Objects.isNull(list)) {
                    break;
                }
                Thread.sleep(retryAndDelay.getDelay());
                return TushareCrawlerResult.builder()
                        .request(request)
                        .isSuccess(list.size() > 0 ? function.apply(list) : true)
                        .build();
            }
            catch (Exception ex) {
                log.error("Request failed: {}, error={}", request.toJSONString(), ex.getMessage());
                try {
                    Thread.sleep(5000L);
                    if (ex instanceof TushareException te) {
                        if (te.isFatal()) {
                            // 设置 Redis 缓存，设置 Fatal 指纹
                            bucket.set(1);
                            return TushareCrawlerResult.builder()
                                    .request(request)
                                    .isSuccess(false)
                                    .isFatal(true)
                                    .msg(ex.getMessage())
                                    .build();
                        }
                        Matcher m = MIN_LIMIT_PATTERN.matcher(ex.getMessage());
                        if (m.find()) {
                            Integer minLimit = Integer.parseInt(m.group(1));
                            retryAndDelay.setDelayByMinLimit(minLimit);
                            continue;
                        }
                        
                        m = HOUR_LIMIT_PATTERN.matcher(ex.getMessage());
                        if (m.find()) {
                            Integer hourLimit = Integer.parseInt(m.group(1));
                            retryAndDelay.setDelayByHourLimit(hourLimit);
                            continue;
                        }
                    }
                } catch (InterruptedException e) {
                    log.error(e.getMessage());
                }
            }
        }
        return TushareCrawlerResult.builder().request(request).isSuccess(false).build();
    }
}
