package {{packageName}}.core.{{entity.module}}.service.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.math.BigInteger;
import lombok.extern.slf4j.Slf4j;

import cn.ibizlab.util.security.SpringContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.stereotype.Service;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.Assert;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import cn.ibizlab.util.errors.BadRequestAlertException;
{{#system.enableGlobalTransaction}}
import io.seata.spring.annotation.GlobalTransactional;
{{/system.enableGlobalTransaction}}
import org.springframework.transaction.annotation.Transactional;
import org.springframework.context.annotation.Lazy;
import {{packageName}}.core.{{entity.module}}.domain.{{entity.codeName}};
import {{packageName}}.core.{{entity.module}}.filter.{{entity.codeName}}SearchContext;
import {{packageName}}.core.{{entity.module}}.service.I{{entity.codeName}}Service;
import {{packageName}}.core.{{entity.module}}.mapper.{{entity.codeName}}Mapper;
import cn.ibizlab.util.helper.CachedBeanCopier;
import cn.ibizlab.util.helper.DEFieldCacheMap;
import cn.ibizlab.util.security.AuthenticationUser;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
{{#entity.relEntities}}
import {{packageName}}.core.{{module}}.domain.{{codeName}};
{{#neq storage "NONE"}}
import {{packageName}}.core.{{module}}.service.I{{codeName}}Service;   
{{/neq}}
{{/entity.relEntities}}


/**
 * 实体[{{entity.logicName}}] 服务对象接口实现
 */
@Slf4j
@Service("{{entity.codeName}}Service")
public class {{entity.codeName}}ServiceImpl extends ServiceImpl<{{entity.codeName}}Mapper,{{entity.codeName}}> implements I{{entity.codeName}}Service {

    protected I{{entity.codeName}}Service getProxyService() {
        return SpringContextHolder.getBean(this.getClass());
    }

    {{#entity.relEntities}}
    {{#neq storage "NONE"}}
    @Autowired
    @Lazy
    protected I{{codeName}}Service {{camelCase codeName}}Service;
   
    {{/neq}}
    {{/entity.relEntities}}
    {{#if entity.indexSubDE}}
    @Autowired
    @Lazy
    protected {{packageName}}.core.{{entity.module}}.mapping.{{entity.codeName}}InheritMapping {{camelCase entity.codeName}}InheritMapping;
    {{/if}}

    protected int batchSize = 500;

    public {{entity.codeName}} get({{entity.codeName}} et) {
        {{entity.codeName}} rt = this.baseMapper.selectEntity(et);
        Assert.notNull(rt,"数据不存在,{{entity.logicName}}:"+et.get{{pascalCase entity.keyField.codeName}}());
        BeanUtils.copyProperties(rt, et);
        {{#entity.nesteds}}
            {{#unless listCode}}
                {{#unless columnName}}
        //设置 [{{entityLogicName}}]
        et.set{{pascalCase codeName}}({{camelCase entityCodeName}}Service.selectBy{{pascalCase fkField.codeName}}(et.get{{pascalCase entity.keyField.codeName}}()));
                {{/unless}}
            {{/unless}}
        {{/entity.nesteds}}
        return et;
    }
    
    public List<{{entity.codeName}}> getByEntities(List<{{entity.codeName}}> entities) {
        return this.baseMapper.selectEntities(entities);
    }

    {{#entity.hasReferences}}
    public void fillParentData({{entity.codeName}} et) {
        {{#each entity.references as |reference|}}
        {{#gt reference.relFieldCount 1}}
        if(!ObjectUtils.isEmpty(et.get{{pascalCase reference.fkField.codeName}}())) {
            {{reference.entityCodeName}} {{camelCase reference.entityCodeName}} = et.get{{pascalCase reference.codeName}}();
            if(!ObjectUtils.isEmpty({{camelCase reference.entityCodeName}})) {
                {{#each reference.fields as |field|}}
                {{#neq field.dataType 'PICKUP'}}
                et.set{{pascalCase field.codeName}}({{camelCase reference.entityCodeName}}.get{{pascalCase field.refFieldCodeName}}());   
                {{/neq}}  
                {{/each}}
            }
        }    
        {{/gt}}
        {{/each}}
    }
    {{/entity.hasReferences}}

    public {{entity.codeName}} getDraft({{entity.codeName}} et) {
        {{#entity.hasReferences}}
        fillParentData(et);
        {{/entity.hasReferences}}
        return et;
    }

    public boolean checkKey({{entity.codeName}} et) {
        return this.count(Wrappers.lambdaQuery(et))>0;
    }

    @Override
    {{#eq entity.actions.create.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.create.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean create({{entity.codeName}} et) {
        {{#entity.hasReferences}}
        fillParentData(et);
        {{/entity.hasReferences}}
        {{#entity.isIndexSubDE}}
        if(ObjectUtils.isEmpty(et.get{{pascalCase entity.keyField.codeName}}()))
            et.set{{pascalCase entity.keyField.codeName}}(({{entity.keyField.type.java}})et.getDefaultKey(true));
        {{entity.indexRelation.entityCodeName}} {{camelCase entity.indexRelation.entityCodeName}} = {{camelCase entity.codeName}}InheritMapping.to{{pascalCase entity.indexRelation.entityCodeName}}(et);
        {{#if entity.indexRelation.indexTypePSDEField}}
        {{camelCase entity.indexRelation.entityCodeName}}.set("{{lowerCase entity.indexRelation.indexTypePSDEField.codeName}}","{{entity.indexRelation.typeValue}}");
        {{/if}}
        {{camelCase entity.indexRelation.entityCodeName}}Service.create({{camelCase entity.indexRelation.entityCodeName}});

        {{/entity.isIndexSubDE}}
        {{#if entity.isLogicInheritDE}}
        if(!update(et, (Wrapper) et.getUpdateWrapper(true).eq("{{lowerCase entity.keyField.name}}", et.get{{pascalCase keyField.codeName}}())))
            return false;
        {{else}}
        if(!this.retBool(this.baseMapper.insert(et))) 
            return false;
        {{/if}}
        {{#each entity.nesteds}}
            {{#unless listCode}}
                {{#unless columnName}}
        {{camelCase entityCodeName}}Service.saveBy{{pascalCase fkField.codeName}}(et,et.get{{pascalCase codeName}}());
                {{/unless}}
            {{/unless}}
        {{/each}}
        get(et);
        {{#if entity.hasPSDERsMapping}}
        updateParentData(et);    
        {{/if}}    
        return true;
    }
    {{#eq entity.actions.create.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.create.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean createBatch(List<{{entity.codeName}}> list) {
        {{#if (or entity.actions.create.psDEActionLogics entity.isIndexSubDE)}}
        for ({{entity.codeName}} et : list) {
            {{#if entity.unionKeyMode}}
            getProxyService().save(et);
            {{else}}
            getProxyService().create(et);
            {{/if}}
        }
        {{else}}
            {{#entity.hasReferences}}
        list.forEach(et->fillParentData(et));
            {{/entity.hasReferences}}
            {{#if entity.unionKeyMode}}
        this.saveOrUpdateBatch(list,batchSize);
            {{else}}
        this.saveBatch(list, batchSize);        
            {{/if}}
            {{#if entity.hasPSDERsMapping}}
        updateParentDataBatch(list);    
            {{/if}}    
        {{/if}}
        return true;
    }

    {{#eq entity.actions.update.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.update.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean update({{entity.codeName}} et) {
        {{#entity.hasReferences}}
        fillParentData(et);
        {{/entity.hasReferences}}
        {{#entity.isIndexSubDE}}
        {{entity.indexRelation.entityCodeName}} {{camelCase entity.indexRelation.entityCodeName}} = {{camelCase entity.codeName}}InheritMapping.to{{pascalCase entity.indexRelation.entityCodeName}}(et);
        {{#if entity.indexRelation.indexTypePSDEField}}
        {{camelCase entity.indexRelation.entityCodeName}}.set("{{lowerCase entity.indexRelation.indexTypePSDEField.codeName}}","{{entity.indexRelation.typeValue}}");
        {{/if}}
        {{camelCase entity.indexRelation.entityCodeName}}Service.save({{camelCase entity.indexRelation.entityCodeName}});

        {{/entity.isIndexSubDE}}
        if(!update(et, (Wrapper) et.getUpdateWrapper(true)
            {{#each entity.keyFields as |field|}}
                .eq("{{lowerCase field.name}}", et.get{{pascalCase field.codeName}}())
            {{/each}}
            )) {
            return false;
        }
        {{#each entity.nesteds}}
            {{#unless listCode}}
                {{#unless columnName}}
        {{camelCase entityCodeName}}Service.saveBy{{pascalCase fkField.codeName}}(et,et.get{{pascalCase codeName}}());
                {{/unless}}
            {{/unless}}
        {{/each}}
        get(et);
        {{#if entity.hasPSDERsMapping}}
        updateParentData(et);    
        {{/if}}    
        return true;
    }

    {{#eq entity.actions.update.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.update.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean updateBatch(List<{{entity.codeName}}> list) {
        {{#if (or entity.actions.update.psDEActionLogics entity.isIndexSubDE (not entity.keyField.phisicalDEField))}}
        for ({{entity.codeName}} et : list) {
            getProxyService().update(et);
        }
        {{else}}
            {{#entity.hasReferences}}
        list.forEach(et->fillParentData(et));
            {{/entity.hasReferences}}
        updateBatchById(list, batchSize);
            {{#if entity.hasPSDERsMapping}}
        updateParentDataBatch(list);    
            {{/if}}   
        {{/if}}
        return true;
    }

    {{#eq entity.actions.save.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.save.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean save({{entity.codeName}} et) {
        if(checkKey(et))
            return getProxyService().update(et);
        else
            return getProxyService().create(et);
    }

    {{#eq entity.actions.save.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.save.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean saveBatch(List<{{entity.codeName}}> list) {
        List<{{entity.codeName}}> rt=this.getByEntities(list);
        Set<Serializable> keys=new HashSet<>();
        rt.forEach(et->{
            Serializable key = et.get{{pascalCase entity.keyField.codeName}}();
            if(!ObjectUtils.isEmpty(key))
                keys.add(key);
        });
        List<{{entity.codeName}}> _create=new ArrayList<>();
        List<{{entity.codeName}}> _update=new ArrayList<>();
        list.forEach(et-> {
            Serializable key = et.get{{pascalCase entity.keyField.codeName}}();
            if(keys.contains(key))
                _update.add(et);
            else
                _create.add(et);
        });
        List rtList=new ArrayList<>();
        if(_update.size()>0 && (!getProxyService().updateBatch(_update)))
            return false;
        if(_create.size()>0 && (!getProxyService().createBatch(_create)))
            return false;
        return true;
    }

    {{#eq entity.actions.remove.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.remove.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean remove({{entity.codeName}} et) {
        {{entity.keyField.type.java}} key = et.get{{pascalCase entity.keyField.codeName}}();
        {{#if entity.hasPSDERsMapping}}
        get(et); 
        {{/if}}
        {{#entity.nesteds}}
            {{#unless listCode}}
                {{#unless columnName}}
                    {{#eq removeActionType 1}}
        {{camelCase entityCodeName}}Service.removeBy{{pascalCase fkField.codeName}}(key);
                    {{/eq}}
                    {{#eq removeActionType 2}}
        {{camelCase entityCodeName}}Service.resetBy{{pascalCase fkField.codeName}}(key);
                    {{/eq}}
                    {{#eq removeActionType 3}}
        if(!ObjectUtils.isEmpty({{camelCase entityCodeName}}Service.removeBy{{pascalCase fkField.codeName}}(key)))
            throw new BadRequestAlertException("删除数据失败，当前数据存在关系实体[{{camelCase entityCodeName}}]数据，无法删除!","","");
                    {{/eq}}
                {{/unless}}
            {{/unless}}
        {{/entity.nesteds}}
        {{#entity.isIndexSubDE}}
        {{camelCase entity.indexRelation.entityCodeName}}Service.remove({{camelCase entity.codeName}}InheritMapping.to{{pascalCase entity.indexRelation.entityCodeName}}(et));

        {{/entity.isIndexSubDE}}

        if(!remove(new QueryWrapper<{{entity.codeName}}>()
            {{#each entity.keyFields as |field|}}
                .eq("{{lowerCase field.name}}", et.get{{pascalCase field.codeName}}())
            {{/each}}
            )) {
            return false;
        }
        {{#if entity.hasPSDERsMapping}}
        updateParentData(et);    
        {{/if}}
        return true ;
    }
    
    {{#eq entity.actions.remove.transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq entity.actions.remove.transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean removeBatch(Collection<{{entity.keyField.type.java}}> ids) {
        {{#if (or entity.actions.update.psDEActionLogics entity.isIndexSubDE (not entity.keyField.phisicalDEField) entity.hasPSDERsMapping)}}
        for ({{entity.keyField.type.java}} et : ids) 
            getProxyService().remove(et);
        {{else}}
        if(!removeByIds(ids))
            return false;
        {{/if}}
        return true;
    }

    {{#entity.extActions}}
    {{#eq transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public {{entity.codeName}} {{camelCase codeName}}({{entity.codeName}} et) {
        return et;
    }
    {{#eq transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public boolean {{camelCase codeName}}Batch(List<{{entity.codeName}}> list) {
        return true;
    }

    {{/entity.extActions}}
    {{#entity.dataSets}}
    public Page<{{#if enableGroup}}Map{{else}}{{entity.codeName}}{{/if}}> search{{pascalCase codeName}}({{entity.codeName}}SearchContext context) {
        {{#if entity.hasPSDEWF}}
        Map<String,Map<String,Object>> businesskeys = fillWFTaskContext(context);
        {{/if}}
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<{{#if enableGroup}}Map{{else}}{{entity.codeName}}{{/if}}> pages=baseMapper.search{{pascalCase codeName}}(context.getPages(),context,context.getSelectCond());
        {{#if entity.hasPSDEWF}}
        if(!StringUtils.isEmpty(context.getSrfWF()))
            fillWFParam(pages,businesskeys);
        {{/if}}
        return new PageImpl<{{#if enableGroup}}Map{{else}}{{entity.codeName}}{{/if}}>(pages.getRecords(), context.getPageable(), pages.getTotal());
    }
    public List<{{#if enableGroup}}Map{{else}}{{entity.codeName}}{{/if}}> list{{pascalCase codeName}}({{entity.codeName}}SearchContext context) {
        return baseMapper.list{{pascalCase codeName}}(context,context.getSelectCond());
    }

    {{/entity.dataSets}}
    {{#entity.references}}
    public List<{{entity.codeName}}> selectBy{{pascalCase fkField.codeName}}({{fkField.type.java}} {{camelCase fkField.codeName}}) {
        return baseMapper.selectBy{{pascalCase fkField.codeName}}({{camelCase fkField.codeName}});
    }

    public boolean removeBy{{pascalCase fkField.codeName}}({{fkField.type.java}} {{camelCase fkField.codeName}}) {
        return this.remove(new QueryWrapper<{{entity.codeName}}>().eq("{{lowerCase fkField.name}}",{{camelCase fkField.codeName}}));
    }

    public boolean resetBy{{pascalCase fkField.codeName}}({{fkField.type.java}} {{camelCase fkField.codeName}}) {
        return this.update(new UpdateWrapper<{{entity.codeName}}>().set("{{lowerCase fkField.name}}",null).eq("{{lowerCase fkField.name}}",{{camelCase fkField.codeName}}));
    }

    public boolean saveBy{{pascalCase fkField.codeName}}({{entityCodeName}} {{camelCase entityCodeName}},List<{{entity.codeName}}> list) {
        if(list==null)
            return true;
        Set<{{entity.keyField.type.java}}> delIds=new HashSet<{{entity.keyField.type.java}}>();
        List<{{entity.codeName}}> _update=new ArrayList<{{entity.codeName}}>();
        List<{{entity.codeName}}> _create=new ArrayList<{{entity.codeName}}>();
        for({{entity.codeName}} before:selectBy{{pascalCase fkField.codeName}}({{camelCase entityCodeName}}.get{{pascalCase relEntity.keyField.codeName}}())){
            delIds.add(before.get{{pascalCase entity.keyField.codeName}}());
        }
        for({{entity.codeName}} sub:list) {
            sub.set{{pascalCase fkField.codeName}}({{camelCase entityCodeName}}.get{{pascalCase relEntity.keyField.codeName}}());
            sub.set{{pascalCase codeName}}({{camelCase entityCodeName}});
            if(ObjectUtils.isEmpty(sub.get{{pascalCase entity.keyField.codeName}}()))
                sub.set{{pascalCase entity.keyField.codeName}}(({{entity.keyField.type.java}})sub.getDefaultKey(true));
            if(delIds.contains(sub.get{{pascalCase entity.keyField.codeName}}())) {
                delIds.remove(sub.get{{pascalCase entity.keyField.codeName}}());
                _update.add(sub);
            }
            else
                _create.add(sub);
        }
        if(_update.size()>0 && (!getProxyService().updateBatch(_update)))
            return false;
        if(_create.size()>0 && (!getProxyService().createBatch(_create)))
            return false;
        if(delIds.size()>0 && (!getProxyService().removeBatch(delIds)))
            return false;
        return true;
    }

    {{/entity.references}}


    {{#if entity.hasPSDEWF}}
    @Autowired
    private cn.ibizlab.util.client.IBZWFFeignClient ibzwfFeignClient;

    /**
     * 查询统一工作流待办
     * @param context
     */
    private Map<String,Map<String,Object>> fillWFTaskContext({{entity.codeName}}SearchContext context){
        Map<String, Map<String, Object>> keys = null;
        Collection<String> businessKeys = null;
        if(!StringUtils.isEmpty(context.getSrfWF())){
            keys= ibzwfFeignClient.getMyTask("{{lowerCase system.codeName}}", "", "{{pluralize entity.codeName}}");
            if(!ObjectUtils.isEmpty(keys))
                businessKeys = keys.keySet();
        }
        else if(!StringUtils.isEmpty(context.getUserTaskId()) && !StringUtils.isEmpty(context.getProcessDefinitionKey()))
            businessKeys= ibzwfFeignClient.getbusinesskeysByUserId("{{lowerCase system.codeName}}", AuthenticationUser.getAuthenticationUser().getUserid(),"{{pluralize entity.codeName}}",context.getProcessDefinitionKey(),context.getUserTaskId());
        if(!ObjectUtils.isEmpty(businessKeys))
            context.getSelectCond().in("{{entity.keyField.columnName}}",businessKeys);
        else
            context.getSelectCond().apply("1<>1");
        return keys;
    }

    /**
     * 填充工作流参数
     * @param pages
     * @param businesskeys
     */
    private void fillWFParam(com.baomidou.mybatisplus.extension.plugins.pagination.Page<{{entity.codeName}}> pages, Map<String, Map<String, Object>> businesskeys) {
        if (!ObjectUtils.isEmpty(businesskeys)) {
            for ({{entity.codeName}} entity : pages.getRecords()) {
                Object id = entity.get{{pascalCase entity.keyField.codeName}}();
                if (!ObjectUtils.isEmpty(id) && businesskeys.containsKey(id)) {
                    Map<String, Object> params = businesskeys.get(id);
                    for (Map.Entry<String, Object> entry : params.entrySet()) {
                        entity.set(entry.getKey(), entry.getValue());
                    }
                }
            }
        }
    }
        
    {{/if}}

}