package com.lanyuanxiaoyao.service.common.utils;

import com.lanyuanxiaoyao.service.common.Constants;
import com.lanyuanxiaoyao.service.common.entity.Record;
import com.lanyuanxiaoyao.service.common.entity.TableMeta;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Record 工具类
 *
 * @author ZhangJiacheng
 * @version 0.0.1
 * @date 2021-12-03
 */
public class RecordHelper {
    public static Boolean isNotVersionUpdateRecord(String record) {
        return !isVersionUpdateRecord(record);
    }

    public static Boolean isVersionUpdateRecord(String record) {
        return record.contains(Constants.VERSION_UPDATE_KEY);
    }

    public static Boolean isNotVersionUpdateRecord(Record record) {
        return !isVersionUpdateRecord(record);
    }

    public static Boolean isVersionUpdateRecord(Record record) {
        //  Record{source=Source{sourceId='versionUpdate', sourceType='null', sourcePos='null', currentTs='2022-11-15 22:17:44'}, statement=Statement{schema='crm_ivpn_cust', table='customer', opStatement='null', opType='version', op='null', opTs='2022-11-15 00:17:43', version='20220925', before=null, after=null}}
        return Constants.VERSION_UPDATE_KEY.equals(record.getSource().getSourceId())
                && Constants.VERSION_KEY.equals(record.getStatement().getOpType());
    }

    /**
     * 获取当前的 Statement, 即 source 和 target 选不为空的操作
     *
     * @param record 消息
     * @return 更新的字段内容
     */
    public static Map<String, Object> getCurrentStatement(Record record) {
        Map<String, Object> before = record.getStatement().getBefore();
        Map<String, Object> after = record.getStatement().getAfter();
        return (after == null ? before : after.isEmpty() ? before : after);
    }

    public static Optional<Map<String, Object>> getBeforeStatement(Record record) {
        return Optional.ofNullable(record.getStatement().getBefore());
    }

    public static Optional<Map<String, Object>> getAfterStatement(Record record) {
        return Optional.ofNullable(record.getStatement().getAfter());
    }

    private static Boolean isMapEmpty(Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    public static Boolean isChangeField(TableMeta meta, Record record, Function<TableMeta, Optional<String>> fieldGetter) {
        Map<String, Object> before = record.getStatement().getBefore();
        Map<String, Object> after = record.getStatement().getAfter();
        Optional<String> field = fieldGetter.apply(meta);
        if (isMapEmpty(before) || isMapEmpty(after) || !field.isPresent()) {
            return false;
        }
        Object beforeField = before.getOrDefault(field.get(), null);
        Object afterField = after.getOrDefault(field.get(), null);
        if (beforeField == null || afterField == null) {
            return false;
        }
        return !Objects.equals(String.valueOf(beforeField), String.valueOf(afterField));
    }

    public static Map<String, Object> addExtraMetadata(Map<String, Object> current, TableMeta tableMeta, Record record) {
        String operationType = record.getStatement().getOpType();
        return addExtraMetadata(current, tableMeta, record, Constants.DELETE.equals(operationType));
    }

    public static Map<String, Object> addExtraMetadata(Map<String, Object> current, TableMeta tableMeta, Record record, Boolean isDelete) {
        Map<String, Object> newMap = new HashMap<>(current);
        newMap.put(Constants.UNION_KEY_NAME, RecordHelper.createUnionKey(tableMeta, current));
        newMap.put(Constants.UPDATE_TIMESTAMP_KEY_NAME, SnowFlakeHelper.next());
        newMap.put(Constants.LATEST_OPERATION_TIMESTAMP_KEY_NAME, record.getStatement().getOpTs());
        newMap.put(Constants.HUDI_DELETE_KEY_NAME, isDelete);
        return newMap;
    }

    public static String createUnionKey(TableMeta tableMeta, Record record) {
        return createUnionKey(tableMeta, getCurrentStatement(record));
    }

    private static final String PRIMARY_KEY_NOT_FOUND = "Primary Key Not Found";

    /**
     * 生成基于主键和分片键的联合主键
     *
     * @param tableMeta 表信息
     * @param fields    字段
     * @return 联合主键值
     */
    public static String createUnionKey(TableMeta tableMeta, Map<String, Object> fields) {
        if (tableMeta.getPrimaryKeys().isEmpty()) {
            throw new RuntimeException(PRIMARY_KEY_NOT_FOUND);
        }
        if (Objects.isNull(fields)) {
            throw new RuntimeException("Fields cannot be null");
        }
        List<String> primaryKeys = tableMeta.getPrimaryKeys()
                .stream()
                .map(key -> Optional.ofNullable(fields.get(Constants.FIELD_COVERT.apply(tableMeta, key.getName())))
                        .orElseThrow(() -> new RuntimeException(PRIMARY_KEY_NOT_FOUND + " " + fields))
                        .toString())
                .collect(Collectors.toList());
        String primaryKey = String.join("-", primaryKeys);
        if (tableMeta.getPartitionKeys().isEmpty()) {
            return primaryKey;
        } else {
            List<String> partitionKeys = tableMeta.getPartitionKeys()
                    .stream()
                    .map(key -> fields.get(Constants.FIELD_COVERT.apply(tableMeta, key.getName())).toString())
                    .collect(Collectors.toList());
            String partitionKey = String.join("-", partitionKeys);
            return primaryKey + "_" + partitionKey;
        }
    }
}
