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.model.DSLink;
import cn.ibizlab.core.data.model.POSchema;
import cn.ibizlab.core.data.model.PojoSchema;
import cn.ibizlab.core.data.service.*;
import cn.ibizlab.util.errors.BadRequestAlertException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

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

    
    
    @Autowired
    @Lazy
    private DbPersistentServiceImpl dbProxyService;

    @Autowired
    @Lazy
    private MongoPersistentServiceImpl mongoProxyService;

    @Autowired
    @Lazy
    private EsPersistentServiceImpl esProxyService;

    @Autowired
    @Lazy
    private ModelService modelService;



    public IPersistentService getProxyService(DSLink link)
    {
        return modelService.getProxyService(link);
    }

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

    @Override
    public DSLink getDSLink(String datasource) {
        return modelService.getDSLink(datasource);
    }

    @Override
    public ResponseData call(DOModel model,String scope, DSLink link, String method, RequestData requestBody)
    {
        return null;
    }

    @Override
    public BaseData create(DOModel model, String scope, DSLink link, BaseData et)
    {
        model.fillParentKey(et).fillDefaultValue(et,true);
        Serializable key=model.getKeyValue(et,true);
        Assert.notNull(key,"未找到主键");
        POSchema poSchema=model.getPOSchema(link.getType());
        return poSchema.trans(getProxyService(link).create(link,poSchema,poSchema.trans2PO(et),true));
    }

    @Override
    public List<BaseData> createBatch(DOModel model, String scope, DSLink link, List<BaseData> list)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        list.forEach(et-> {
                    model.fillParentKey(et).fillDefaultValue(et, true);
                    Serializable key = model.getKeyValue(et, true);
                    Assert.notNull(key,"未找到主键");
                });
        return getProxyService(link).createBatch(link,poSchema,poSchema.trans2PO(list),false);
    }

    @Override
    public BaseData update(DOModel model, String scope, DSLink link, BaseData et)
    {
        model.fillParentKey(et).fillDefaultValue(et,false);
        Serializable key=model.getKeyValue(et,false);
        Assert.notNull(key,"未找到主键");
        POSchema poSchema=model.getPOSchema(link.getType());
        return poSchema.trans(getProxyService(link).update(link,poSchema,poSchema.trans2PO(et),true));
    }

    @Override
    public List<BaseData> updateBatch(DOModel model, String scope, DSLink link, List<BaseData> list)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        list.forEach(et-> {
            model.fillParentKey(et).fillDefaultValue(et, false);
            Serializable key = model.getKeyValue(et, false);
            Assert.notNull(key,"未找到主键");
        });
        return getProxyService(link).updateBatch(link,poSchema,poSchema.trans2PO(list),false);
    }

    @Override
    public boolean remove(DOModel model, String scope, DSLink link, Serializable key)
    {
        Assert.notNull(key,"未找到主键");
        BaseData et = model.newData(key);
        Assert.notNull(et,"未找到主键");
        POSchema poSchema=model.getPOSchema(link.getType());
        return getProxyService(link).removeByMap(link,poSchema,poSchema.trans2PO(et));
    }

    @Override
    public boolean removeBatch(DOModel model, String scope, DSLink link, List<Serializable> idList)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        List<BaseData> batch=new ArrayList<>();
        idList.forEach(key->{
            BaseData et = poSchema.newData(key);
            Assert.notNull(et,"未找到主键");
            batch.add(et);
        });
        return getProxyService(link).removeByMapBatch(link,poSchema,poSchema.trans2PO(batch));
    }

    @Override
    public boolean removeByMap(DOModel model, String scope, DSLink link, BaseData et)
    {
        Serializable key=model.getKeyValue(et,false);
        Assert.notNull(et,"未找到主键");
        POSchema poSchema=model.getPOSchema(link.getType());
        return getProxyService(link).removeByMap(link,poSchema,poSchema.trans2PO(et));
    }

    @Override
    public boolean removeByMapBatch(DOModel model, String scope, DSLink link, List<BaseData> list) {
        POSchema poSchema=model.getPOSchema(link.getType());
        list.forEach(et->{
            Serializable key = model.getKeyValue(et, true);
            Assert.notNull(key,"未找到主键");
        });
        return getProxyService(link).removeByMapBatch(link,poSchema,poSchema.trans2PO(list));
    }

    @Override
    public BaseData get(DOModel model, String scope, DSLink link, Serializable key)
    {
        Assert.notNull(key,"未找到主键");
        BaseData et = model.newData(key);
        Assert.notNull(et,"未找到主键");
        POSchema poSchema=model.getPOSchema(link.getType());
        return poSchema.trans(getProxyService(link).getByMap(link,poSchema,poSchema.trans2PO(et)));
    }

    @Override
    public List<BaseData> getBatch(DOModel model, String scope, DSLink link, List<Serializable> idList)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        List<BaseData> batch=new ArrayList<>();
        idList.forEach(key->{
            BaseData et = poSchema.newData(key);
            Assert.notNull(et,"未找到主键");
            batch.add(et);
        });
        return poSchema.trans(getProxyService(link).getByMapBatch(link,poSchema,poSchema.trans2PO(batch)));
    }

    @Override
    public BaseData getByMap(DOModel model, String scope, DSLink link, BaseData et)
    {
        Serializable key=model.getKeyValue(et,false);
        Assert.notNull(key,"未找到主键");
        POSchema poSchema=model.getPOSchema(link.getType());
        return poSchema.trans(getProxyService(link).getByMap(link,poSchema,poSchema.trans2PO(et)));
    }

    @Override
    public List<BaseData> getByMapBatch(DOModel model, String scope, DSLink link, List<BaseData> list) {
        POSchema poSchema=model.getPOSchema(link.getType());
        list.forEach(et->{
            Serializable key = model.getKeyValue(et, true);
            Assert.notNull(key,"未找到主键");
        });
        return poSchema.trans(getProxyService(link).getByMapBatch(link,poSchema,poSchema.trans2PO(list)));
    }

    @Override
    public BaseData getDraft(DOModel model, String scope, DSLink link, BaseData et)
    {
        model.fillParentKey(et);
        if(!ObjectUtils.isEmpty(et.getParentDatas()))
        {
            et.getParentDatas().entrySet().forEach(entry->{
                String refSchemaKey=entry.getKey();
                BaseData parent=entry.getValue();
                if(parent.keySet().size()==0)
                    return;
                PojoSchema refSchema=model.getSchema().getRefSchema(refSchemaKey);
                Map<String,PojoSchema> refProperties=refSchema.getRefProperties();
                if(refProperties.size()<=1)
                    return;

                if((!StringUtils.isEmpty(refSchema.getName()))&&(!StringUtils.isEmpty(parent.getKey())))
                {
                    String system=StringUtils.isEmpty(refSchema.getSystem())?model.getSystemId():refSchema.getSystem();
                    try{
                        BaseData parentData=this.getByMap(system,scope,refSchema.getOptions().getEntityName(),"",parent);
                        if(parentData!=null)
                        {
                            refProperties.entrySet().forEach(propEntry->{
                                PojoSchema prop=propEntry.getValue();
                                String refFieldCodeName=prop.getOptions().getRefFieldCodeName();
                                if((!StringUtils.isEmpty(refFieldCodeName))&&parentData.get(refFieldCodeName.toLowerCase())!=null)
                                    et.set(propEntry.getKey().toLowerCase(), parentData.get(refFieldCodeName.toLowerCase()));
                            });
                        }

                    }catch (Exception ex){log.error("填充上级数据失败"+refSchema.getOptions().getEntityName());}

                }

            });
        }

        return et;

    }

    @Override
    public boolean checkKey(DOModel model, String scope, DSLink link, BaseData et)
    {
        Serializable key=model.getKeyValue(et,false);
        if(ObjectUtils.isEmpty(key))
            return false;
        POSchema poSchema=model.getPOSchema(link.getType());
        return getProxyService(link).checkKey(link,poSchema,poSchema.trans2PO(et));
    }

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

    @Override
    public List<BaseData> saveBatch(DOModel model, String scope, DSLink link, List<BaseData> list)
    {
        POSchema poSchema=model.getPOSchema(link.getType());

        List<BaseData> rt=poSchema.trans(getProxyService(link).getBatchKey(link,poSchema,poSchema.trans2PO(list)));

        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);
        });
        List rtList=new ArrayList<>();
        if(update.size()>0)
            rtList.addAll(this.updateBatch(model,scope,link,update));
        if(create.size()>0)
            rtList.addAll(this.createBatch(model,scope,link,create));

        return rtList;
    }

    @Override
    public List<BaseData> select(DOModel model, String scope, DSLink link, BaseData et)
    {
        model.fillParentKey(et);
        POSchema poSchema=model.getPOSchema(link.getType());
        return poSchema.trans(getProxyService(link).select(link,poSchema,poSchema.trans2PO(et)));
    }

    @Override
    public Page<BaseData> select(DOModel model, String scope, DSLink link, BaseData et, Pageable pageable)
    {
        model.fillParentKey(et);
        POSchema poSchema=model.getPOSchema(link.getType());
        Page<BaseData> pages = getProxyService(link).select(link,poSchema,poSchema.trans2PO(et),pageable);
        poSchema.trans(pages.getContent());
        return pages;
    }

    @Override
    public List<BaseData> query(DOModel model, String scope, DSLink link, String dataQuery, FilterData context)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        model.fillParentFilter(context);
        context.setPOSchema(poSchema);
        return poSchema.trans(getProxyService(link).query(link,poSchema,dataQuery,context));
    }

    @Override
    public Page<BaseData> query(DOModel model, String scope, DSLink link, String dataQuery, FilterData context, Pageable pageable)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        model.fillParentFilter(context);
        context.setPOSchema(poSchema);
        if(pageable!=null)
            context.setPageable(pageable);
        else
            pageable=context.getPageable();
        Page<BaseData> pages = getProxyService(link).query(link,poSchema,dataQuery,context,pageable);
        poSchema.trans(pages.getContent());
        return pages;
    }

    @Override
    public List<BaseData> selectRaw(DOModel model, String scope, DSLink link, String sql, FilterData context)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        return getProxyService(link).selectRaw(link,poSchema,sql,context);
    }

    @Override
    public boolean execRaw(DOModel model, String scope, DSLink link, String sql, BaseData param)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        return getProxyService(link).execRaw(link,poSchema,sql,param);
    }

    @Override
    public List<BaseData> fetch(DOModel model, String scope, DSLink link, String dataSet, FilterData context)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        model.fillParentFilter(context);
        context.setPOSchema(poSchema);
        return poSchema.trans(getProxyService(link).fetch(link,poSchema,dataSet,context));
    }

    @Override
    public Page<BaseData> fetch(DOModel model, String scope, DSLink link, String dataSet, FilterData context, Pageable pageable)
    {
        POSchema poSchema=model.getPOSchema(link.getType());
        model.fillParentFilter(context);
        context.setPOSchema(poSchema);
        if(pageable!=null)
            context.setPageable(pageable);
        else
            pageable=context.getPageable();
        Page<BaseData> pages = getProxyService(link).fetch(link,poSchema,dataSet,context,pageable);
        poSchema.trans(pages.getContent());
        return pages;
    }


}


