package com.lanyuanxiaoyao.service.sync.functions;

import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.service.common.entity.FlinkJob;
import com.lanyuanxiaoyao.service.common.entity.Record;
import com.lanyuanxiaoyao.service.common.entity.TableMeta;
import com.lanyuanxiaoyao.service.common.utils.RecordHelper;
import com.lanyuanxiaoyao.service.sync.configuration.GlobalConfiguration;
import com.lanyuanxiaoyao.service.sync.utils.JacksonUtils;
import com.lanyuanxiaoyao.service.sync.utils.StatusUtils;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.runtime.state.FunctionInitializationContext;
import org.apache.flink.runtime.state.FunctionSnapshotContext;
import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.flink.streaming.api.checkpoint.CheckpointedFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Pulsar message to object
 *
 * @author ZhangJiacheng
 * @date 2022-06-11
 */
public class PulsarMessage2RecordFunction extends RichMapFunction<String, Record> implements CheckpointedFunction {
    private static final Logger logger = LoggerFactory.getLogger(PulsarMessage2RecordFunction.class);
    private static final AtomicReference<String> lastOperationTime = new AtomicReference<>("");
    private final static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final Pattern OPTS_PATTERN = Pattern.compile("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$");
    private final GlobalConfiguration globalConfiguration;
    private final FlinkJob flinkJob;
    private final TableMeta tableMeta;
    private final ObjectMapper mapper = JacksonUtils.getMapper();

    public PulsarMessage2RecordFunction(GlobalConfiguration globalConfiguration, FlinkJob flinkJob, TableMeta tableMeta) {
        this.globalConfiguration = globalConfiguration;
        this.flinkJob = flinkJob;
        this.tableMeta = tableMeta;
    }

    @Override
    public Record map(String message) throws JsonProcessingException {
        Record record = null;
        try {
            record = mapper.readValue(message, Record.class);
            if (RecordHelper.isNotVersionUpdateRecord(record)) {
                lastOperationTime.set(record.getStatement().getOpTs());
            }
        } catch (Exception exception) {
            logger.error("Message json parse failure", exception);
        }
        return record;
    }

    @Override
    public void snapshotState(FunctionSnapshotContext context) {
        String opTs = lastOperationTime.get();
        Long timestamp = null;
        try {
            if (StrUtil.isNotBlank(opTs) && OPTS_PATTERN.matcher(opTs).matches()) {
                timestamp = LocalDateTime.parse(opTs, FORMATTER).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
            } else {
                throw new Exception("Opts is not match regex " + OPTS_PATTERN.pattern());
            }
        } catch (Exception e) {
            logger.error("Parse operation time error", e);
        }
        StatusUtils.syncOperation(globalConfiguration, flinkJob, tableMeta, timestamp);
    }

    @Override
    public void initializeState(FunctionInitializationContext context) {
    }
}
