package com.lanyuanxiaoyao.service.executor.task.functions.police;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.service.common.Constants;
import com.lanyuanxiaoyao.service.common.entity.Record;
import com.lanyuanxiaoyao.service.executor.core.TaskContext;
import com.lanyuanxiaoyao.service.executor.task.entity.Prisoner;
import com.lanyuanxiaoyao.service.executor.task.entity.RecordView;
import com.lanyuanxiaoyao.service.executor.task.helper.JacksonHelper;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.collections.api.factory.Lists;

/**
 * @author lanyuanxiaoyao
 */
public class PulsarMessage2Prisoner extends RichMapFunction<RecordView, Prisoner> {
    private static final 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 ObjectMapper mapper = JacksonHelper.getMapper();

    private final long startTime;
    private final long endTime;
    private final String[] primaryKeys;
    private final String[] partitionKeys;

    public PulsarMessage2Prisoner(long startTime, long endTime, String[] primaryKeys, String[] partitionKeys) {
        this.startTime = startTime;
        this.endTime = endTime;
        this.primaryKeys = primaryKeys;
        this.partitionKeys = partitionKeys;
    }

    @Override
    public Prisoner map(RecordView value) {
        Record record;
        try {
            record = mapper.readValue(value.getData(), Record.class);
        } catch (Throwable e) {
            return new Prisoner(value.getFile(), StrUtil.format("{}: {}", e.getMessage(), value.getData()));
        }
        Record.Statement statement = record.getStatement();
        if (ObjectUtil.isNull(statement) || StrUtil.isBlank(statement.getOpTs())) {
            return new Prisoner(value.getFile(), StrUtil.format("Invalid statement: {}", value.getData()));
        }
        long timestamp;
        try {
            if (OPTS_PATTERN.matcher(statement.getOpTs()).matches()) {
                timestamp = LocalDateTime.parse(statement.getOpTs(), FORMATTER).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
            } else {
                throw new Exception("opts not match " + OPTS_PATTERN.pattern());
            }
        } catch (Throwable e) {
            return new Prisoner(value.getFile(), StrUtil.format("Invalid timestamp: {}", value.getData()));
        }
        if (timestamp < startTime || timestamp > endTime) {
            return null;
        }
        Map<String, Object> fields;
        if (StrUtil.equalsAny(statement.getOpType(), Constants.INSERT, Constants.UPDATE)) {
            fields = record.getStatement().getAfter();
        } else if (StrUtil.equals(statement.getOpType(), Constants.DELETE)) {
            fields = record.getStatement().getBefore();
        } else {
            return new Prisoner(value.getFile(), StrUtil.format("Invalid opType: {}", value.getData()));
        }
        String primaryKey = Lists.immutable.of(primaryKeys).select(fields::containsKey).collect(fields::get).makeString(",");
        String partitionKey = Lists.immutable.of(partitionKeys).select(fields::containsKey).collect(fields::get).makeString(",");
        if (StrUtil.isBlank(primaryKey)) {
            return new Prisoner(value.getFile(), StrUtil.format("Invalid primaryKey: {}", value.getData()));
        }
        if (StrUtil.isBlank(partitionKey)) {
            return new Prisoner(value.getFile(), StrUtil.format("Invalid partitionKey: {}", value.getData()));
        }
        return new Prisoner(value.getFile(), record.getStatement().getOpType(), primaryKey, partitionKey, timestamp);
    }
}
