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

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.StockHolderDao;
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.StockHolder;
import link.at17.mid.tushare.data.models.interfaces.ITsStockInfo;
import link.at17.mid.tushare.data.models.interfaces.ITsTradeDate;
import link.at17.mid.tushare.enums.StockHolderType;
import link.at17.mid.tushare.service.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class StockHolderService extends BaseServiceImpl<StockHolderDao, StockHolder> implements ITsTradeDate {

    @Autowired
    RedissonClient redis;

    @Autowired
    TushareCrawler tushareCrawler;

    @Autowired
    StockInfoService stockInfoService;


    /**
     * <b><a href="https://tushare.pro/document/2?doc_id=61">前十大股东</a>/<a href="https://tushare.pro/document/2?doc_id=62">前十大流通股东</a></b><br>
     * <blockquote>
     * 接口：top10_holders/top10_floatholders<br>
     * 更新时间：不定时，报告日<br>
     * 描述：获取上市公司前十大股东数据，包括持有数量和比例等信息/获取上市公司前十大流通股东数据，不包括比例信息<br>
     * </blockquote>
     * 文档说单次 100 条限制，但实际测试有单次 5000 条<br>
     * <font color="red">请先更新交易日历和股票列表后再调用该方法</font><br>
     * <font color="red">如果是 A+H 或者 A+B 股，可能存在同股票存在多个同名股东，且持股数据不一致。需要仔细研究如何清洗和使用</font>
     */
    // TODO: A+H 或者 A+B 股是否还需要重命名？
    @UpdateMethod(name="十大股东数据", order=10)
    public boolean updateData() {
        
        StockHolderType[] holderTypes = new StockHolderType[] {
                StockHolderType.TOP10,
                StockHolderType.TOP10Float
        };
        
        for (StockHolderType holderType : holderTypes) {

            boolean isFloat = holderType.getIsFloat() == 1;
            TushareRequestBody baseRequest = new TushareRequestBody(isFloat ? "top10_floatholders" : "top10_holders");
            Function<List<JSONObject>, Boolean> function = (t) -> {
                Map<String, JSONObject> map = new HashMap<>();
                for (JSONObject jo : t) {
                    String holderName = jo.getString("holder_name");
                    String[] temp = {jo.getString("ts_code"), jo.getString("end_date"), holderName, holderType.name()};
                    String key = String.join("_", temp);
                    Integer holderOffset = 1;
                    while (map.containsKey(key)) {
                        log.warn("存在重复的 key {}，开始重命名", key);
                        temp[2] = holderName + '^' +  holderOffset++;
                        jo.put("holder_name", temp[2]);
                        key = String.join("_", temp);
                    }
                    map.put(key, jo);
                }
                insertOrUpdateList(t, holderType);
                return true;
            };
            List<Future<TushareCrawlerResult>> executeResult;
            executeResult = tushareCrawler.rollingQueryByStock(baseRequest, stockInfoService.list(), baseMapper, function, 5000L, QueryWay.ByStock, null);
            String banner = "十大" + (isFloat ? "流通股东" : "股东");
            log.info("{}数据更新完成", banner);
            for (Future<TushareCrawlerResult> f : executeResult) {
                try {
                    TushareCrawlerResult result = f.get();
                    JSONObject request = f.get().getRequest();
                    if (result.isFatal()) {
                        log.error("{}数据未获取，发生致命错误，将不会重试：{}", banner, request.toJSONString());
                    }
                    else if (!result.isSuccess()) {
                        log.info("{}数据未获取：{}", banner, request.toJSONString());
                    }
                } catch (InterruptedException | ExecutionException e) {
                    log.error("检查" + banner + "数据执行结果时发生错误", e);
                }
            }
            if (baseRequest.hasFingerprint()) {
                redis.getBucket(baseRequest.getFingerprint()).delete();
            }
        }
        
        return true;
    }

    /**
     * 插入从 Tushare 抓取的内容
     * 
     * @param list
     * @return
     */
    public boolean insertOrUpdateList(List<JSONObject> list, StockHolderType holderType) {
        return SqlHelper.retBool(baseMapper.insertOrUpdateList(list, holderType));
    }

    @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);
    }

}
