package com.pictc.datalog;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.pictc.annotations.datalog.LogField;
import com.pictc.annotations.datalog.LogJoin;
import com.pictc.annotations.datalog.LogTable;
import com.pictc.utils.BeanUtils;
import com.pictc.utils.CollectionUtils;
import com.pictc.utils.MybatisTools;
import com.pictc.utils.SpringAnnotationUtils;
import com.pictc.utils.StringUtils;

import cn.hutool.core.bean.BeanUtil;
import lombok.Data;

@Data
public class LogTableInfo {
	
	private LogTable table;
	
	private Field idField;
	
	private TableInfo info;
	
	private List<LogFieldInfo> fields = CollectionUtils.newArrayList();
	
	private Map<String,LogFieldInfo> fieldMap = CollectionUtils.newConcurrentHashMap();
	
	private Map<String,LogFieldInfo> columnMap = CollectionUtils.newConcurrentHashMap();
	
	private List<LogJoinInfo> joins = CollectionUtils.newArrayList();
	
	private Class<?> klazz;

	public LogTableInfo(Class<?> klazz) {
		super();
		this.klazz = klazz;
		table = SpringAnnotationUtils.findAnnotation(klazz, LogTable.class);
		info = MybatisTools.getTableInfo(table.source());
		initId();
		initFields();
		initJoins();
	}

	public String getName() {
		return table.name();
	}

	public boolean isValid() {
		return table!=null;
	}
	
	private void initId() {
		String idKey = info.getKeyProperty();
		if(StringUtils.isEmpty(idKey)) {
			idKey = "id";
		}
		idField = BeanUtils.getField(idKey,klazz);
	}
	
	private void initFields() {
		if(isValid()) {
			List<Field> _fields = BeanUtils.getFields(klazz, LogField.class);
			if(CollectionUtils.isNotEmpty(_fields)) {
				Map<String, TableFieldInfo> fieldMap = null;
				if(info!=null) {
					fieldMap = info.getFieldList().stream().collect(Collectors.toMap(TableFieldInfo::getProperty,Function.identity()));
				}
				for (Field field : _fields) {
					LogFieldInfo fieldInfo = LogFieldInfo.ofField(field);
					if(fieldMap!=null) fieldInfo.setFieldInfo(fieldMap.get(field.getName()));
					fields.add(fieldInfo);
				}
				Collections.sort(fields);
				this.fieldMap = fields.stream().collect(Collectors.toMap(LogFieldInfo::getFieldName,Function.identity()));
				this.columnMap = fields.stream().collect(Collectors.toMap(LogFieldInfo::getColumn,Function.identity()));
			}
		}
	}
	
	private void initJoins() {
		if(isValid()) {
			List<Field> _fields = BeanUtils.getFields(klazz, LogJoin.class);
			if(CollectionUtils.isNotEmpty(_fields)) {
				for (Field field : _fields) {
					fields.add(LogFieldInfo.ofField(field));
					joins.add(LogJoinInfo.ofAnnotation(klazz, field));
				}
			}
		}
	}
	
	public Long getIdValue(Object obj) {
		return BeanUtils.getFieldValue(idField,obj);
	}
	
	public void setIdValue(Object entity, Long id) {
		if(entity==null) return;
		BeanUtils.setFieldValue(idField,id,entity);
	}
	

	public Object getFieldValue(Object entity,String field) {
		if(entity==null) return null;
		LogFieldInfo fieldInfo = this.fieldMap.get(field);
		return BeanUtils.getFieldValue(fieldInfo.getField(),entity);
	}
	
	public Object getFieldValue(Object entity,Field field) {
		if(entity==null) return null;
		return BeanUtils.getFieldValue(field,entity);
	}
	
	
	public void setFieldValue(Object entity,String field,Object value) {
		if(entity==null) return;
		LogFieldInfo fieldInfo = this.fieldMap.get(field);
		BeanUtils.setFieldValue(fieldInfo.getField(),value,entity);
	}
	
	public void setFieldValue(Object entity,Field field,Object value) {
		if(entity==null) return;
		BeanUtils.setFieldValue(field,value,entity);
	}


	public String getColumn(String field) {
		LogFieldInfo fieldInfo = this.fieldMap.get(field);
		return fieldInfo.getColumn();
	}
	
	
	public String getIdColumn() {
		LogFieldInfo fieldInfo = this.fieldMap.get(idField.getName());
		return fieldInfo.getColumn();
	}

	public Class<?> getEntityType() {
		return info.getEntityType();
	}
	
	public Object toEntity(Object dto) {
		return BeanUtil.toBean(dto, info.getEntityType());
	}
	
	
	public Object toDto(Object entity) {
		return BeanUtil.toBean(entity,klazz);
	}


	public String getTableName() {
		return info.getTableName();
	}


}
