package quant.rich.emoney.service.sqlite;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;

import lombok.extern.slf4j.Slf4j;
import quant.rich.emoney.annotation.ResponseDecodeExtension;
import quant.rich.emoney.entity.sqlite.StrategyAndPool;
import quant.rich.emoney.mapper.sqlite.StrategyAndPoolMapper;

@DS("sqlite")
@Service
@Slf4j
public class StrategyAndPoolService extends SqliteServiceImpl<StrategyAndPoolMapper, StrategyAndPool> {
    
    @Autowired
    ObjectMapper objectMapper;
    
    /**
     * 从数据库中导入所有已记录的策略和池，形成可供益盟 Fiddler 插件使用的 prepared_strategies.json
     * @return
     */
    public JsonNode exportPreparedStrategies() {
        List<StrategyAndPool> all = this.list();
        Collections.sort(all);
        
        Integer currentStrategyId = null;
        ArrayNode preparedStrategies = objectMapper.createArrayNode();
        ObjectNode current = null;
        
        for (StrategyAndPool strategyAndPool : all) {
            if (currentStrategyId == null || Integer.compare(currentStrategyId, strategyAndPool.getStrategyId()) != 0) {
                
                if (current != null) {
                    preparedStrategies.add(current);
                }
                
                currentStrategyId = strategyAndPool.getStrategyId();
                // 意味着要新增一个 current 来容纳同 strategyId 的所有 pool
                current = objectMapper.createObjectNode();
                current.put("code", "strategy_" + strategyAndPool.getStrategyId());
                current.put("name", strategyAndPool.getStrategyName() + "策略");
                current.put("type", 0);
                current.put("blockGroupLogic", 0);
                current.put("isLocked", false);
                current.put("matchType", 0);
                current.put("startDate", 0);
                current.put("endDate", 0);
                current.putArray("fields");
            }
            ObjectNode pool = objectMapper.createObjectNode();
            pool.put("code", "pool_" + strategyAndPool.getPoolId());
            pool.put("name", strategyAndPool.getPoolName());
            pool.put("isLocked", false);
            pool.put("isDisabled", false);
            pool.put("valueType", "Switch");
            ((ArrayNode)current.get("fields")).add(pool);
        }
        if (current != null) {
            preparedStrategies.add(current);
        }
        return preparedStrategies;
    }
    
    /**
     * 从解析后的 Nano 转换的 JsonNode 转换成 StrategyAndPool 的集合，并存储到数据库中
     * @param jsonNode
     */
    @ResponseDecodeExtension(protocolId="9400")
    @Async
    public void updateByQueryResponseAsync(JsonNode jsonNode) {
        // jsonNode.output[].band[]/.tech[]/.val[]
        
        if (jsonNode == null) {
            log.warn("试图更新 StrategyAndPool, 但提供的响应 json 为空");
            return;
        }
        
        JsonNode output = jsonNode.get("output");
        if (output == null || output.getNodeType() != JsonNodeType.ARRAY) {
            log.warn("试图更新 StrategyAndPool, 但提供的响应 json.data.output 为 null 或不为 ARRAY");
            return;
        }
        
        Set<StrategyAndPool> set = new HashSet<>();
        
        String[] types = new String[] {"band", "tech", "val"}; // 波段/技术/基本面(价值)
        for (JsonNode node : output) {
            for (String type : types) {
                JsonNode strategies = node.get(type);
                if (strategies != null && strategies.getNodeType() == JsonNodeType.ARRAY) {
                    for (JsonNode simpleStrategy : strategies) {
                        StrategyAndPool strategyAndPool = new StrategyAndPool(
                                type,
                                simpleStrategy.get("strategyName").asText(),
                                simpleStrategy.get("strategyId").asInt(),
                                simpleStrategy.get("poolName").asText(),
                                simpleStrategy.get("poolId").asInt()
                                );
                        if (!set.contains(strategyAndPool) && !exists(
                            new LambdaQueryWrapper<StrategyAndPool>()
                                .eq(StrategyAndPool::getPoolId, strategyAndPool.getPoolId()))) {
                            set.add(strategyAndPool);
                        }
                    }
                }
            }
        }
        this.saveOrUpdateBatch(set);
        log.info("新增 {} 条 StrategyAndPool", set.size());
    }
    
}
