package com.tmerclub.cloud.search.canal.support.parser;

import com.tmerclub.cloud.search.canal.annotation.CanalField;
import com.tmerclub.cloud.search.canal.annotation.CanalModel;
import com.tmerclub.cloud.search.canal.common.NamingPolicy;
import com.tmerclub.cloud.search.canal.model.ModelTable;
import com.tmerclub.cloud.search.canal.support.parser.converter.BaseCanalFieldConverter;
import com.tmerclub.cloud.search.canal.support.parser.converter.CanalFieldConvertInput;
import com.tmerclub.cloud.search.canal.support.parser.converter.CanalFieldConvertResult;
import com.tmerclub.cloud.search.canal.support.parser.converter.CanalFieldConverterFactory;
import com.tmerclub.cloud.search.canal.util.AssertUtils;
import com.tmerclub.cloud.search.canal.util.ReflectionUtils;
import com.tmerclub.cloud.search.canal.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.sql.JDBCType;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author throwable
 * @version v1
 * @description
 * @since 2020/9/24 0:56
 */
public class InMemoryModelTableMetadataManager implements ModelTableMetadataManager {

    private final ConcurrentMap<Class<?>, ModelTableMetadata> cache = new ConcurrentHashMap<>(16);

    @Autowired
    private CanalFieldConverterFactory canalFieldConverterFactory;

    public InMemoryModelTableMetadataManager(CanalFieldConverterFactory canalFieldConverterFactory) {
        this.canalFieldConverterFactory = canalFieldConverterFactory;
    }

    public static InMemoryModelTableMetadataManager of(CanalFieldConverterFactory canalFieldConverterFactory) {
        return new InMemoryModelTableMetadataManager(canalFieldConverterFactory);
    }

    @Override
    public ModelTableMetadata load(Class<?> klass) {
        return cache.computeIfAbsent(klass, clazz -> {
            AssertUtils.X.isTrue(klass.isAnnotationPresent(CanalModel.class), String.format("[%s]没有使用@CanalModel注解", klass.getName()));
            CanalModel canalModel = klass.getAnnotation(CanalModel.class);
            NamingPolicy namingPolicy = canalModel.fieldNamingPolicy();
            ModelTableMetadata metadata = new ModelTableMetadata();
            metadata.setModelTable(ModelTable.of(canalModel.database(), canalModel.table()));
            Map<String, ColumnMetadata> fieldColumnMapping = new HashMap<>(8);
            ReflectionUtils.X.doWithFields(klass, field -> {
                CanalField canalField;
                JDBCType sqlType = null;
                Class<? extends BaseCanalFieldConverter<?>> converterKlass = null;
                String columnName = null;
                if (field.isAnnotationPresent(CanalField.class)) {
                    canalField = field.getAnnotation(CanalField.class);
                    sqlType = canalField.sqlType();
                    converterKlass = canalField.converterKlass();
                    if (StringUtils.X.isNotEmpty(canalField.columnName())) {
                        columnName = canalField.columnName();
                    }
                }
                String fieldName = field.getName();
                if (null == columnName) {
                    columnName = namingPolicy.convert(fieldName);
                }
                CanalFieldConvertInput input = CanalFieldConvertInput.builder()
                        .fieldKlass(field.getType())
                        .sqlType(sqlType)
                        .converterKlass(converterKlass)
                        .build();
                CanalFieldConvertResult result = canalFieldConverterFactory.load(input);
                ColumnMetadata columnMetadata = new ColumnMetadata();
                columnMetadata.setColumnName(columnName);
                columnMetadata.setConverter(result.getConverter());
                fieldColumnMapping.put(fieldName, columnMetadata);
            });
            metadata.setFieldColumnMapping(fieldColumnMapping);
            return metadata;
        });
    }
}
