package cn.ibizlab.core.lite.extensions.service;

import cn.ibizlab.core.extensions.service.DstDataSourceExService;
import cn.ibizlab.core.lite.extensions.domain.EntityModel;
import cn.ibizlab.core.lite.extensions.domain.EntityObj;
import cn.ibizlab.core.lite.extensions.domain.FieldModel;
import cn.ibizlab.core.lite.extensions.domain.ModelObj;
import cn.ibizlab.core.lite.extensions.model.DataModel;
import cn.ibizlab.core.lite.extensions.model.Property;
import cn.ibizlab.core.lite.service.IMetaModelService;
import cn.ibizlab.util.filter.QueryFilter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.util.*;

@Slf4j
@Service
public class LiteDataService {

    @Autowired
    private DbEntityService dbEntityService;

    @Autowired
    private MongoEntityService mongoEntityService;

    @Autowired
    private IMetaModelService metaModelService;


    @Autowired
    @Lazy
    private SqlSessionFactory sqlSessionFactory;


    @Autowired
    private DstDataSourceExService dstDataSourceService;

    @Autowired
    private LiteModelService liteModelService;



    public CommonEntityService getEntityService(String dsId)
    {
        String dsType=dstDataSourceService.initDataSource(dsId);
        if("mongodb".equalsIgnoreCase(dsType))
            return mongoEntityService;
        else
            return dbEntityService;
    }





    public void processDataModel(String metaModelId,Timestamp lastModify,LiteDataCallback callback)
    {
        DataModel dataModel= JSON.toJavaObject(JSON.parseObject(metaModelService.get(metaModelId).getConfig()), DataModel.class);
        EntityModel entityModel = dataModel.getFactEntityModel();
        getEntityService(entityModel.getDsName()).processList(dataModel,lastModify,callback);
    }


    public ModelObj getModelObjs(String metaModelId, Set<String> fillPropertys, Object factKey) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        List<EntityObj> kEntityObjs = new ArrayList<>();
        kEntityObjs.add(new EntityObj().set(dataModel.getFactEntityModel().getKeyField().getColumnName(), factKey));
        List<ModelObj> list = getModelObjs(dataModel, fillPropertys, kEntityObjs);
        if (list.size() > 0)
            return list.get(0);
        return null;
    }

    public ModelObj getModelObjs(String metaModelId, Set<String> fillPropertys, Object... factUniKey) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        List<EntityObj> kEntityObjs = new ArrayList<>();
        EntityObj entityObj = new EntityObj();
        List<FieldModel> uniKeys = dataModel.getFactEntityModel().getUnionKeyFields();
        for (int i = 0; i < uniKeys.size(); i++)
            entityObj.set(uniKeys.get(i).getColumnName(), factUniKey[i]);
        kEntityObjs.add(entityObj);
        List<ModelObj> list = getModelObjs(dataModel, fillPropertys, kEntityObjs);
        if (list.size() > 0)
            return list.get(0);
        return null;
    }

    public List<ModelObj> getModelObjs(String metaModelId, Set<String> fillPropertys, List<EntityObj> kEntityObjs) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        return getModelObjs(dataModel, fillPropertys, kEntityObjs);
    }

    public List<ModelObj> getModelObjs(DataModel dataModel, Set<String> fillPropertys, List<EntityObj> kEntityObjs) {
        EntityModel entityModel = dataModel.getFactEntityModel();
        List<ModelObj> rt = new ArrayList<>();
        List<EntityObj> factEntityList = new ArrayList<>();
        if (fillPropertys == null || fillPropertys.size() == 0 || fillPropertys.contains(dataModel.getFactPorperty().getPropertyName()))
            splitList(kEntityObjs, 1000).forEach(list -> factEntityList.addAll(getEntityService(entityModel.getDsName()).selectBase(entityModel, QueryFilter.createQuery().cust(dataModel.lookup(list)))));
        factEntityList.forEach(entityObj ->
                rt.add(new ModelObj().setDataModel(dataModel).setFactEntity(entityObj.setProperty(dataModel.getFactPorperty()))));
        fillEntityObj(dataModel, fillPropertys, rt);
        dataModel.getNestedDataModels().forEach(subModel ->
                this.getModelObjs(subModel, fillPropertys, factEntityList).forEach(subObj -> subObj.findParent(rt)));
        rt.forEach(modelObj -> modelObj.getTimestamp());
        return rt;
    }

    public void fillEntityObj(DataModel dataModel, Set<String> fillPropertys, List<ModelObj> modelObjs) {
        if (dataModel.getObjectProperties().size() > 1) {
            for (Property property : dataModel.getObjectProperties()) {
                if (property.getPropertyName().equals(dataModel.getFactPorperty().getPropertyName()))
                    continue;
                EntityModel entityModel = property.getEntityModel();
                List<EntityObj> entityObjs = new ArrayList<>();
                if (fillPropertys == null || fillPropertys.size() == 0 || fillPropertys.contains(property.getPropertyName()))
                    splitList(modelObjs, 1000).forEach(list -> entityObjs.addAll(getEntityService(entityModel.getDsName()).selectBase(entityModel, QueryFilter.createQuery().cust(property.lookup(list)))));

                entityObjs.forEach(entityObj -> {
                    entityObj.setProperty(property);
                    entityObj.findModel(modelObjs);
                });
            }
        }
    }


    private <T> List<List<T>> splitList(List<T> list, int groupSize) {
        int length = list.size();
        // 计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize; // TODO
        List<List<T>> newList = new ArrayList<>(num);
        for (int i = 0; i < num; i++) {
            // 开始位置
            int fromIndex = i * groupSize;
            // 结束位置
            int toIndex = (i + 1) * groupSize < length ? (i + 1) * groupSize : length;
            newList.add(list.subList(fromIndex, toIndex));
        }
        return newList;
    }


}
