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

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Function;

import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;

import link.at17.mid.tushare.annotation.UpdateMethod;
import link.at17.mid.tushare.dao.StockDailyBasicDao;
import link.at17.mid.tushare.data.crawler.QueryWay;
import link.at17.mid.tushare.data.crawler.tushare.TushareCrawler;
import link.at17.mid.tushare.data.crawler.tushare.TushareCrawlerResult;
import link.at17.mid.tushare.data.crawler.tushare.TushareRequestBody;
import link.at17.mid.tushare.data.models.StockValueEx;
import link.at17.mid.tushare.data.models.interfaces.ITsStockInfo;
import link.at17.mid.tushare.data.models.interfaces.ITsTradeDate;
import link.at17.mid.tushare.data.validator.AllowedEnum;
import link.at17.mid.tushare.service.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class StockDailyBasicService extends BaseServiceImpl<StockDailyBasicDao, StockValueEx> implements ITsTradeDate {

    @Autowired
    StockInfoService stockInfoService;
    
    @Autowired
    TushareCrawler tushareCrawler;
    
    @Autowired
    RedissonClient redis;
    
    /**
     * 更新由 Tushare 获取的数据
     * @param list
     * @return
     */
    public boolean insertOrUpdateList(List<JSONObject> list) {
        return SqlHelper.retBool(baseMapper.insertOrUpdateList(list));
    }
    
    @Override
    public LocalDateTime getLatestTradeDate(ITsStockInfo stockInfo) {
        return baseMapper.getLatestTradeDate(stockInfo);
    }

    @Override
    public List<LocalDateTime> getAllTradeDates(ITsStockInfo stockInfo) {
        return baseMapper.getAllTradeDates(stockInfo);
    }

    @Override
    public List<LocalDateTime> getAllMissingDates(ITsStockInfo stockInfo) {
        return baseMapper.getAllMissingDates(stockInfo);
    }

    /**
     * 获取个股每日指标
     * <blockquote>
     * 接口：daily_basic<br>
     * 更新时间：交易日每日15点～17点之间<br>
     * 描述：获取全部股票每日重要的基本面指标，可用于选股分析、报表展示等。<br>
     * 积分：用户需要至少600积分才可以调取，具体请参阅
     * <a href="https://tushare.pro/document/1?doc_id=13">积分获取办法</a><br>
     * </blockquote>
     */
    @UpdateMethod(name="每日基本指标", order=3)
    public boolean updateData(
            @AllowedEnum({"ByDateUpdate", "ByStockCrossCheck"})
            QueryWay queryWay) {
        TushareRequestBody baseRequest = new TushareRequestBody("daily_basic");
        baseRequest.addFields("ts_code,trade_date,close,turnover_rate,turnover_rate_f,volume_ratio,pe,pe_ttm,pb,ps,ps_ttm,dv_ratio,dv_ttm,total_share,float_share,free_share,total_mv,circ_mv");
        Function<List<JSONObject>, Boolean> function = (t) -> {
            insertOrUpdateList(t);
            return true;
        };
        List<Future<TushareCrawlerResult>> executeResult;
        if (queryWay.compareTo(QueryWay.ByStock) >= 0) {
            executeResult = tushareCrawler.rollingQueryByStock(baseRequest, stockInfoService.list(), this, function, 5000L, queryWay, null);
        }
        else {
            executeResult = tushareCrawler.rollingQueryByDate(baseRequest, this, function, queryWay, null);
        }
        log.info("每日指标数据更新完成");
        for (Future<TushareCrawlerResult> f : executeResult) {
            try {
                TushareCrawlerResult result = f.get();
                JSONObject request = f.get().getRequest();
                if (result.isFatal()) {
                    log.error("每日指标数据未获取，发生致命错误，将不会重试：{}", request.toJSONString());
                }
                else if (!result.isSuccess()) {
                    log.warn("每日指标数据未获取：{}", request.toJSONString());
                }
            } catch (InterruptedException | ExecutionException e) {
                log.error("检查每日指标数据执行结果时发生错误", e);
            }
        }
        if (baseRequest.hasFingerprint()) {
            redis.getBucket(baseRequest.getFingerprint()).delete();
        }
        return true;
    }

}
