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 org.springframework.util.StringUtils;

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 boolean saveBatch(EntityModel entityModel, String dsName, List<EntityObj> list) {
        if(!getEntityService(StringUtils.isEmpty(dsName)?entityModel.getDsName():dsName).saveBatch(dsName,entityModel,list))
            return false;
        return true;
    }

    public boolean saveBatch(String systemId, String entityName, String dsName, List<EntityObj> list) {
        EntityModel entityModel = liteModelService.getEntityModel(systemId,entityName);
        if(!getEntityService(StringUtils.isEmpty(dsName)?entityModel.getDsName():dsName).saveBatch(dsName,entityModel,list))
            return false;
        return true;
    }

    public boolean save(String systemId, String entityName, String dsName, EntityObj entityObj) {
        EntityModel entityModel = liteModelService.getEntityModel(systemId,entityName);
        if(StringUtils.isEmpty(dsName))
            dsName=entityModel.getDsName();
        return getEntityService(dsName).save(dsName,entityModel,entityObj);
    }

    public EntityObj get(String systemId, String entityName, String dsName, EntityObj entityObj) {
        EntityModel entityModel = liteModelService.getEntityModel(systemId,entityName);
        if(StringUtils.isEmpty(dsName))
            dsName=entityModel.getDsName();
        return getEntityService(dsName).get(dsName,entityModel,entityObj);
    }

    public List<EntityObj> search(String systemId, String entityName, String dsName, QueryFilter filter) {
        return search(systemId,entityName,"BASE",dsName,filter);
    }

    public List<EntityObj> search(String systemId, String entityName, String dataset, String dsName, QueryFilter filter) {
        EntityModel entityModel = liteModelService.getEntityModel(systemId,entityName);
        if(StringUtils.isEmpty(dsName))
            dsName=entityModel.getDsName();
        return getEntityService(dsName).search(dsName,dataset,entityModel,filter);
    }

    public void searchCursor(String systemId, String entityName, String sql, String dsName, QueryFilter filter,LiteDataCallback callback) {
        EntityModel entityModel = liteModelService.getEntityModel(systemId,entityName);
        if(StringUtils.isEmpty(dsName))
            dsName=entityModel.getDsName();
        searchCursor(entityModel,sql,dsName,filter,callback);
    }

    public void searchCursor(EntityModel entityModel, String sql, String dsName, QueryFilter filter,LiteDataCallback callback) {
        if(StringUtils.isEmpty(dsName))
            dsName=entityModel.getDsName();
        getEntityService(dsName).cursorRead(dsName,entityModel,sql,filter,callback);
    }

    public void processDataModel(String metaModelId,String extCond, Timestamp lastModify,LiteDataCallback callback)
    {
        DataModel dataModel= liteModelService.getDataModel(metaModelId);
        EntityModel entityModel = dataModel.getFactEntityModel();
        getEntityService(entityModel.getDsName()).processList(dataModel,extCond,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<>();
        List<List<EntityObj>> splist=splitList(kEntityObjs, 1000);
        if (fillPropertys == null || fillPropertys.size() == 0 || fillPropertys.contains(dataModel.getFactPorperty().getPropertyName()))
            splist.forEach(list -> factEntityList.addAll(getEntityService(entityModel.getDsName()).selectBase(entityModel.getDsName(), 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;
    }

    /**
     * 主业务数据支持自定义sql过滤条件
     * @param metaModelId
     * @param fillPropertys
     * @param kEntityObjs
     * @param extCond
     * @return
     */
    public List<ModelObj> getModelObjs2(String metaModelId, Set<String> fillPropertys, List<EntityObj> kEntityObjs, String extCond) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        EntityModel entityModel = dataModel.getFactEntityModel();
        List<ModelObj> rt = new ArrayList<>();
        List<EntityObj> factEntityList = new ArrayList<>();
        List<List<EntityObj>> splist=splitList(kEntityObjs, 1000);
        if (fillPropertys == null || fillPropertys.size() == 0 || fillPropertys.contains(dataModel.getFactPorperty().getPropertyName()))
            splist.forEach(list -> factEntityList.addAll(getEntityService(entityModel.getDsName()).selectBase(entityModel.getDsName(), entityModel,
                    QueryFilter.createQuery().cust(dataModel.lookup(list)+(StringUtils.hasLength(extCond)?" and "+extCond:"")))));
        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) {
        List<List<ModelObj>> splist=splitList(modelObjs, 1000);
        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()))
                    splist.forEach(list -> entityObjs.addAll(getEntityService(entityModel.getDsName()).selectBase(entityModel.getDsName(), entityModel, QueryFilter.createQuery().cust(property.lookup(list)))));

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



    public static <T> List<List<T>> splitList(List<T> list, int count){
        List<List<T>> lists = new ArrayList<List<T>>(); // 结果集

        int listSize = list.size(); // 原集合长度
        int listCount = listSize / count; // 可直接拆分集合数量

        // 按量拆分
        for (int i = 0; i < listCount ; i++) {
            List<T> sub2=new ArrayList<>();
            for(int j=i*count;j<(i + 1) * count;j++)
                sub2.add(list.get(j));
            lists.add(sub2);
        }

        // 如果按量拆分后还有剩余, 收尾
        int remainder = listSize % count;
        if(remainder > 0) {
            List<T> sub2=new ArrayList<>();
            for(int j=listSize - remainder;j<listSize;j++)
                sub2.add(list.get(j));
            lists.add(sub2);
        }

        return lists;
    }


}
