package cn.ibizlab.core.data.service.impl;

import cn.ibizlab.core.data.domain.DOModel;
import cn.ibizlab.core.data.dto.*;
import cn.ibizlab.core.data.mapper.DbDataMapper;
import cn.ibizlab.core.data.model.DSLink;
import cn.ibizlab.core.data.model.POSchema;
import cn.ibizlab.core.data.service.IDSSettingService;
import cn.ibizlab.core.data.service.IDataService;
import cn.ibizlab.core.data.service.ModelService;
import cn.ibizlab.util.errors.BadRequestAlertException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.*;

/**
 * 实体[业务实体] 无存储服务对象接口实现
 */
@Slf4j
@Service
public class DbDataServiceImpl implements IDataService {

    @Value("${ibiz.data.batchsize:500}")
    private int BATCH_SIZE;

    @Autowired
    private DbDataMapper dbDataMapper;


    @Autowired
    private IDSSettingService dsSettingService;

    @Override
    public ResponseData call(DOModel model, String scope, String datasource, String method, RequestData requestData)
    {
        return null;
    }

    @Override
    public boolean create(DOModel model, String scope, String datasource, BaseData et)
    {
        Serializable key=model.getKeyValue(et,true);
        if(ObjectUtils.isEmpty(key))
            throw new BadRequestAlertException("未找到主键",model.getName(),null);

        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());

        if(dbDataMapper.insertData(link.getName(),poSchema,et)==1)
            et.setAll(get(model,scope,datasource,key));
        return true;
    }

    @Override
    public void createBatch(DOModel model, String scope, String datasource, List<BaseData> list)
    {
        List<BaseData> batch=new ArrayList<>();
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        list.forEach(et->{
            Serializable key=model.getKeyValue(et,true);
            if(ObjectUtils.isEmpty(key))
                throw new BadRequestAlertException("未找到主键",model.getName(),null);
            batch.add(et);
            if(batch.size()>=BATCH_SIZE)
            {
                dbDataMapper.insertBathData(link.getName(),poSchema,batch);
                batch.clear();
            }
        });
        if(batch.size()>=0)
        {
            dbDataMapper.insertBathData(link.getName(),poSchema,batch);
            batch.clear();
        }
    }

    @Override
    public boolean update(DOModel model, String scope, String datasource, BaseData et)
    {
        Serializable key=model.getKeyValue(et,false);
        if(ObjectUtils.isEmpty(key))
            throw new BadRequestAlertException("未找到主键",model.getName(),null);
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        dbDataMapper.updateData(link.getName(),poSchema,et);
        et.setAll(get(model,scope,datasource,(Serializable)key));
        return true;
    }

    @Override
    public void updateBatch(DOModel model, String scope, String datasource, List<BaseData> list)
    {
        List<BaseData> batch=new ArrayList<>();
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        list.forEach(et->{
            Serializable key=model.getKeyValue(et,false);
            if(ObjectUtils.isEmpty(key))
                throw new BadRequestAlertException("未找到主键",model.getName(),null);
            batch.add(et);
            if(batch.size()>=BATCH_SIZE)
            {
                dbDataMapper.updateBathData(link.getName(),poSchema,batch);
                batch.clear();
            }
        });
        if(batch.size()>=0)
        {
            dbDataMapper.updateBathData(link.getName(),poSchema,batch);
            batch.clear();
        }
    }

    @Override
    public boolean remove(DOModel model, String scope, String datasource, Serializable key)
    {
        if(ObjectUtils.isEmpty(key))
            throw new BadRequestAlertException("未找到主键",model.getName(),null);
        BaseData et = model.newData(key);
        if(et==null)
            throw new BadRequestAlertException("未找到主键",model.getName(),null);
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        dbDataMapper.removeData(link.getName(),poSchema,et);
        return true;
    }

    @Override
    public void removeBatch(DOModel model, String scope, String datasource, List<Serializable> idList)
    {
        List<BaseData> batch=new ArrayList<>();
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        idList.forEach(key->{
            BaseData et = model.newData(key);
            if(et==null)
                throw new BadRequestAlertException("未找到主键",model.getName(),null);
            batch.add(et);
            if(batch.size()>=BATCH_SIZE)
            {
                dbDataMapper.removeBathData(link.getName(),poSchema,batch);
                batch.clear();
            }
        });
        if(batch.size()>=0)
        {
            dbDataMapper.removeBathData(link.getName(),poSchema,batch);
            batch.clear();
        }
    }

    @Override
    public boolean removeByMap(DOModel model, String scope, String datasource, BaseData et)
    {
        Serializable key=model.getKeyValue(et,false);
        if(ObjectUtils.isEmpty(key))
            throw new BadRequestAlertException("未找到主键",model.getName(),null);
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        dbDataMapper.removeData(link.getName(),poSchema,et);
        return true;
    }

    @Override
    public BaseData get(DOModel model, String scope, String datasource, Serializable key)
    {
        if(ObjectUtils.isEmpty(key))
            throw new BadRequestAlertException("未找到主键",model.getName(),null);
        BaseData et = model.newData(key);
        if(et==null)
            throw new BadRequestAlertException("未找到主键",model.getName(),null);
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        List<BaseData> list=dbDataMapper.getData(link.getName(),poSchema,et);
        if(ObjectUtils.isEmpty(list)||list.size()>1)
            throw new BadRequestAlertException("未找到数据",model.getName(),key.toString());
        return poSchema.trans(list.get(0));
    }

    @Override
    public List<BaseData> getBatch(DOModel model, String scope, String datasource, List<Serializable> idList)
    {
        List<BaseData> rt=new ArrayList<>();
        List<BaseData> batch=new ArrayList<>();
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        idList.forEach(key->{
            BaseData et = model.newData(key);
            if(et==null)
                throw new BadRequestAlertException("未找到主键",model.getName(),null);
            batch.add(et);
            if(batch.size()>=BATCH_SIZE)
            {
                rt.addAll(dbDataMapper.getBatchData(link.getName(),poSchema,batch));
                batch.clear();
            }
        });
        if(batch.size()>=0)
        {
            rt.addAll(dbDataMapper.getBatchData(link.getName(),poSchema,batch));
            batch.clear();
        }
        return poSchema.trans(rt);
    }

    @Override
    public BaseData getByMap(DOModel model, String scope, String datasource, BaseData et)
    {
        Serializable key=model.getKeyValue(et,false);
        if(ObjectUtils.isEmpty(key))
            throw new BadRequestAlertException("未找到主键",model.getName(),null);
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        List<BaseData> list=dbDataMapper.getData(link.getName(),poSchema,et);
        if(ObjectUtils.isEmpty(list)||list.size()>1)
            throw new BadRequestAlertException("未找到数据",model.getName(),key.toString());
        return poSchema.trans(list.get(0));
    }

    @Override
    public BaseData getDraft(DOModel model, String scope, String datasource, BaseData et)
    {
        return null;
    }

    @Override
    public boolean checkKey(DOModel model, String scope, String datasource, BaseData et)
    {
        Serializable key=model.getKeyValue(et,false);
        if(ObjectUtils.isEmpty(key))
            return false;

        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        int cnt=dbDataMapper.countData(link.getName(),poSchema,et);
        return cnt==1;
    }

    @Override
    public boolean save(DOModel model, String scope, String datasource, BaseData et)
    {
        if(checkKey(model,scope,datasource,et))
            return update(model,scope,datasource,et);
        else
            return create(model,scope,datasource,et);
    }

    @Override
    public void saveBatch(DOModel model, String scope, String datasource, List<BaseData> list)
    {

        List<BaseData> rt=new ArrayList<>();
        List<BaseData> batch=new ArrayList<>();
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        list.forEach(et->{
            Serializable key=model.getKeyValue(et,true);
            if(ObjectUtils.isEmpty(key))
                throw new BadRequestAlertException("未找到主键",model.getName(),null);
            batch.add(et);
            if(batch.size()>=BATCH_SIZE)
            {
                rt.addAll(dbDataMapper.getBatchKey(link.getName(),poSchema,batch));
                batch.clear();
            }
        });
        if(batch.size()>=0)
        {
            rt.addAll(dbDataMapper.getBatchKey(link.getName(),poSchema,batch));
            batch.clear();
        }
        Map<Serializable,Integer> keys=new LinkedHashMap<>();
        rt.forEach(et->{
            Serializable key=model.getKeyValue(et,false);
            if(!ObjectUtils.isEmpty(key))
                keys.put(key,1);
        });

        List<BaseData> create=new ArrayList<>();
        List<BaseData> update=new ArrayList<>();

        list.forEach(et-> {
            Serializable key = model.getKeyValue(et, false);
            if(keys.containsKey(key))
                update.add(et);
            else
                create.add(et);
        });
        if(update.size()>0)
            this.updateBatch(model,scope,datasource,update);
        if(create.size()>0)
            this.createBatch(model,scope,datasource,create);

    }

    @Override
    public List<BaseData> select(DOModel model, String scope, String datasource, BaseData et)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        return poSchema.trans(dbDataMapper.selectData(link.getName(),poSchema,et));
    }

    @Override
    public Page<BaseData> select(DOModel model, String scope, String datasource, BaseData et, Pageable pageable)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<BaseData> pages=dbDataMapper.selectData(link.getName(),poSchema,et,FilterData.getPages(poSchema,pageable));
        return new PageImpl<BaseData>(poSchema.trans(pages.getRecords()), pageable, pages.getTotal());
    }

    @Override
    public List<BaseData> query(DOModel model, String scope, String datasource, String dataQuery, FilterData context)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        context.setPOSchema(poSchema);
        return poSchema.trans(dbDataMapper.queryData(link.getName(),poSchema,context.getSql("-dq-"+dataQuery+"-"),context,context.getSearchCond()));
    }

    @Override
    public Page<BaseData> query(DOModel model, String scope, String datasource, String dataQuery, FilterData context, Pageable pageable)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        context.setPOSchema(poSchema);
        if(pageable!=null)
            context.setPageable(pageable);
        else
            pageable=context.getPageable();
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<BaseData> pages=
                dbDataMapper.queryData(link.getName(),poSchema,context.getSql("-dq-"+dataQuery+"-"),context,context.getSearchCond(),context.getPages());
        return new PageImpl<BaseData>(poSchema.trans(pages.getRecords()), pageable, pages.getTotal());
    }

    @Override
    public List<BaseData> selectRaw(DOModel model, String scope, String datasource, String sql, FilterData context)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        return dbDataMapper.queryData(link.getName(),poSchema,sql,context,context.getSearchCond());
    }

    @Override
    public boolean execRaw(DOModel model, String scope, String datasource, String sql, BaseData param)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        return dbDataMapper.executeRaw(link.getName(),poSchema,sql,param);
    }

    @Override
    public List<BaseData> fetch(DOModel model, String scope, String datasource, String dataSet, FilterData context)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        context.setPOSchema(poSchema);
        return poSchema.trans(dbDataMapper.queryData(link.getName(),poSchema,context.getSql("-ds-"+dataSet+"-"),context,context.getSearchCond()));
    }

    @Override
    public Page<BaseData> fetch(DOModel model, String scope, String datasource, String dataSet, FilterData context, Pageable pageable)
    {
        if(StringUtils.isEmpty(datasource))datasource=model.getDefaultDataSource();
        DSLink link=dsSettingService.getDataSource(datasource);
        POSchema poSchema=model.getPOSchema(link.getType());
        context.setPOSchema(poSchema);
        if(pageable!=null)
            context.setPageable(pageable);
        else
            pageable=context.getPageable();
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<BaseData> pages=
                dbDataMapper.queryData(link.getName(),poSchema,context.getSql("-ds-"+dataSet+"-"),context,context.getSearchCond(),context.getPages());
        return new PageImpl<BaseData>(poSchema.trans(pages.getRecords()), pageable, pages.getTotal());
    }

    @Override
    public DOModel getDOModel(String system, String entity)
    {
        return modelService.getDOModel(system,entity);
    }


    @Autowired
    private ModelService modelService;

}


