package com.lutao.cloud.consume.kafka.consumer;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lutao.cloud.collect.vo.DataAddVO;
import com.lutao.cloud.common.core.kafka.MessageCommandEnum;
import com.lutao.cloud.common.core.util.StringUtil;
import com.lutao.cloud.consume.factory.CommandStrategy;
import com.lutao.cloud.consume.factory.CommandStrategyFactory;
import com.lutao.cloud.consume.service.ConsumerLogService;
import com.lutao.entity.CollectLog;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.annotation.KafkaHandler;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;


/**
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
@Component
@Slf4j
@KafkaListener(topics = {"oceancrude_dynamic_data_topic"}, groupId = "dynamic-consumer-group")
@AllArgsConstructor
public class DynamicConsumer {

    private CommandStrategyFactory commandStrategyFactory;

    private Map<String, List<DataAddVO>> dataMap = new ConcurrentHashMap<>();

    private final ReentrantLock lock = new ReentrantLock();

    private List<DataAddVO> addVOS = new ArrayList<>();

    @Resource
    private ConsumerLogService collectLogService;


    /**
     * 接收消息处理
     * SINGLE_MESSAGE 单条消息类型
     * ROLE_GROUP_MESSAGE 根据角色处理消息
     *
     * @param message
     */
    @KafkaHandler
    //@LogToDatabase
    public void receive(String message) {
        try {
            if (StringUtil.isNotBlank(message)) {
                DataAddVO dataAddVO = convertMessageToEntity(message);

                //需要操作的数据表
                String resource = dataAddVO.getResource();

                //date里面的数据
                JsonNode jsonDate = dataAddVO.getJsonDate();
                //得到命令
                String command = dataAddVO.getCommand();

                if (StringUtil.isBlank(command)) {
                    return;
                }
                //数据库不同，策略不同
                CommandStrategy<Object> strategy = commandStrategyFactory.createStrategy(resource);
                strategy.execute(command, jsonDate, dataAddVO);

            }
        } catch (Exception e) {
            log.error("dynamic-consumer receive message error:{}", e);
            CollectLog collectLog = new CollectLog();
            collectLog.setCommand("error");
            collectLog.setData(e.getMessage() + "===" + message);
            collectLogService.save(collectLog);
            e.printStackTrace();
        }
    }


    public void receiveBatch(String message) {

        if (StringUtil.isNotBlank(message)) {
            log.info("dynamic-consumer receive message:{}", message);
            DataAddVO dataAddVO = convertMessageToEntity(message);

            //需要操作的数据表
            String resource = dataAddVO.getResource();

            //date里面的数据
            JsonNode jsonDate = dataAddVO.getJsonDate();
            //得到命令
            String command = dataAddVO.getCommand();

            if (StringUtil.isBlank(command)) {
                return;
            }
            //resource不同 map分组不同
            if (MessageCommandEnum.MESSAGE_FIND.getCommand().equals(command)) {
                CommandStrategy<Object> strategy = commandStrategyFactory.createStrategy(resource);
                strategy.execute(command, jsonDate, dataAddVO);
            } else {
                lock.lock();
                try {
                    addVOS = dataMap.get(resource + ":" + command);
                    if (jsonDate != null) {
                        addVOS.add(dataAddVO);
                        dataMap.put(resource, addVOS);
                        addVOS.clear();
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    /**
     * 消费数据map
     */
    @SneakyThrows
    // @Scheduled(fixedDelay = 3000)
    public void consumeDataMap() {

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,
                15,
                180,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        CountDownLatch countDownLatch = new CountDownLatch(dataMap.size());

        lock.lock();
        try {
            for (Map.Entry<String, List<DataAddVO>> entry : dataMap.entrySet()) {

                threadPoolExecutor.submit(() -> {

                    String resource = entry.getKey();
                    List<DataAddVO> addVOList = entry.getValue();
                    //通过策略进行批量保存
                    if (addVOList.size() > 0) {
                        CommandStrategy<Object> strategy = commandStrategyFactory.createStrategy(resource);
                        //执行保存操作
                        strategy.executeBatch(addVOList);
                    }
                    //清除List
                    addVOList.clear();

                });
                //闭锁 -1
                countDownLatch.countDown();
            }
        } finally {
            lock.unlock();
        }

        countDownLatch.await();
        threadPoolExecutor.shutdown();
    }

    /**
     * 将消息转换为实体
     *
     * @param message 消息
     */
    public DataAddVO convertMessageToEntity(String message) {
        try {
            //创建ObjectMapper对象
            ObjectMapper objectMapper = new ObjectMapper();
            //将字符串转换为JSON对象
            JsonNode jsonNode = objectMapper.readTree(message);
            //将JSON对象转换为Java对象
            return objectMapper.treeToValue(jsonNode, DataAddVO.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}
