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.util.stream.Collectors;
import java.math.BigInteger;
import lombok.extern.slf4j.Slf4j;

import com.google.common.collect.Lists;
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.{{entity.codeName}}Service;
import {{packageName}}.core.{{entity.module}}.repository.{{entity.codeName}}Repository;
import cn.ibizlab.util.helper.CachedBeanCopier;
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.{{codeName}}Service;
{{/neq}}
{{/entity.relEntities}}


/**
 * 实体[{{entity.logicName}}] 服务对象接口实现
 */
@Slf4j
@Service("{{entity.codeName}}Service")
public class {{entity.codeName}}ServiceBase implements {{entity.codeName}}Service {

    @Autowired
    {{entity.codeName}}Repository {{camelCase entity.codeName}}Repository;

    {{#entity.relEntities}}
    {{#neq storage "NONE"}}
    @Autowired
    @Lazy
    protected {{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) {
        Optional<{{entity.codeName}}> rt = {{camelCase entity.codeName}}Repository.findById(et.get{{pascalCase entity.keyField.codeName}}());
        Assert.notNull(rt.get(),"数据不存在,{{entity.logicName}}:"+et.get{{pascalCase entity.keyField.codeName}}());
        BeanUtils.copyProperties(rt.get(), et);
        {{#entity.nesteds}}
            {{#if listCode}}
                {{#unless columnName}}
        //设置 [{{entityLogicName}}]
        et.set{{pascalCase codeName}}({{camelCase entityCodeName}}Service.selectBy{{pascalCase fkField.codeName}}(et.get{{pascalCase entity.keyField.codeName}}()));
                {{/unless}}
            {{/if}}
        {{/entity.nesteds}}
        return et;
    }
    
    public List<{{entity.codeName}}> getByEntities(List<{{entity.codeName}}> entities) {
        Iterable<{{entity.codeName}}> rts =  {{camelCase entity.codeName}}Repository.findAllById(entities.stream().map(e->e.get{{pascalCase entity.keyField.codeName}}()).collect(Collectors.toList()));
        return Lists.newArrayList(rts);
    }

    {{#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 Integer checkKey({{entity.codeName}} et) {
        {{#entity.hasPhisicalLinkField}}
        fillParentData(et);
        {{/entity.hasPhisicalLinkField}}
        {{#if entity.unionKeyMode}}
        if(ObjectUtils.isEmpty(et.get{{pascalCase entity.keyField.codeName}}()))
            et.set{{pascalCase entity.keyField.codeName}}(({{entity.keyField.type.java}})et.getDefaultKey(true));
        {{/if}}

        return ( (!ObjectUtils.isEmpty(et.get{{pascalCase entity.keyField.codeName}}())) && {{camelCase entity.codeName}}Repository.existsById(et.get{{pascalCase entity.keyField.codeName}}()))?1: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.hasPhisicalLinkField}}
        fillParentData(et);
        {{/entity.hasPhisicalLinkField}}
        {{#if 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}});

        {{else}}
            {{#if entity.unionKeyMode}}
        if(ObjectUtils.isEmpty(et.get{{pascalCase entity.keyField.codeName}}()))
            et.set{{pascalCase entity.keyField.codeName}}(({{entity.keyField.type.java}})et.getDefaultKey(true));
            {{/if}}
        {{/if}}
        {{#if entity.isLogicInheritDE}}
        if(!update(et, (Wrapper) et.getUpdateWrapper(true).eq("{{lowerCase entity.keyField.name}}", et.get{{pascalCase keyField.codeName}}())))
            return false;
        {{else}}
        {{entity.codeName}} rt = {{camelCase entity.codeName}}Repository.insert(et);
        BeanUtils.copyProperties(rt, et);
        {{/if}}
        {{#each entity.nesteds}}
            {{#if listCode}}
                {{#unless columnName}}
        {{camelCase entityCodeName}}Service.saveBy{{pascalCase der.codeName}}(et,et.get{{pascalCase codeName}}());
                {{/unless}}
            {{/if}}
        {{/each}}
        {{#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) {
            getSelf().create(et);
        }
        {{else}}
            {{#entity.hasPhisicalLinkField}}
        list.forEach(et->fillParentData(et));
            {{/entity.hasPhisicalLinkField}}
        {{#if entity.unionKeyMode}}
        list.forEach(et -> {
            if(ObjectUtils.isEmpty(et.get{{pascalCase entity.keyField.codeName}}()))
                et.set{{pascalCase entity.keyField.codeName}}(({{entity.keyField.type.java}})et.getDefaultKey(true));
            });
        {{/if}}
        list = {{camelCase entity.codeName}}Repository.insert(list);
            {{#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.hasPhisicalLinkField}}
        fillParentData(et);
        {{/entity.hasPhisicalLinkField}}
        {{#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}}
        {{entity.codeName}} rt = {{camelCase entity.codeName}}Repository.save(et);
        BeanUtils.copyProperties(rt, et);
        {{#each entity.nesteds}}
            {{#if listCode}}
                {{#unless columnName}}
        {{camelCase entityCodeName}}Service.saveBy{{pascalCase der.codeName}}(et,et.get{{pascalCase codeName}}());
                {{/unless}}
            {{/if}}
        {{/each}}
        {{#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) {
            getSelf().update(et);
        }
        {{else}}
            {{#entity.hasPhisicalLinkField}}
        list.forEach(et->fillParentData(et));
            {{/entity.hasPhisicalLinkField}}
        list = {{camelCase entity.codeName}}Repository.saveAll(list);
            {{#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)>0)
            return getSelf().update(et);
        else
            return getSelf().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 && (!getSelf().updateBatch(_update)))
            return false;
        if(_create.size()>0 && (!getSelf().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}}
            {{#if 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.selectBy{{pascalCase fkField.codeName}}(key)))
            throw new BadRequestAlertException("删除数据失败，当前数据存在关系实体[{{camelCase entityCodeName}}]数据，无法删除!","","");
                    {{/eq}}
                {{/unless}}
            {{/if}}
        {{/entity.nesteds}}
        {{#entity.isIndexSubDE}}
        {{camelCase entity.indexRelation.entityCodeName}}Service.remove({{camelCase entity.codeName}}InheritMapping.to{{pascalCase entity.indexRelation.entityCodeName}}(et));

        {{/entity.isIndexSubDE}}
        {{camelCase entity.codeName}}Repository.deleteById(key);
        {{#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 removeByEntities(List<{{entity.codeName}}> entities) {
        {{#if (or entity.actions.update.psDEActionLogics entity.isIndexSubDE entity.hasPSDERsMapping)}}
        for ({{entity.codeName}} et : entities) {
            if(!getSelf().remove(et))
                return false;
        }
        return true;
        {{else}}
        {{camelCase entity.codeName}}Repository.deleteAll(entities);
        return true;
        {{/if}}
    }

    {{#entity.extActions}}
    {{#eq transactionMode "GLOBAL"}}@GlobalTransactional{{/eq}}{{#eq transactionMode "DEFAULT"}}@Transactional{{/eq}}
    public {{outParam}} {{camelCase codeName}}({{inParam}} {{inParamName}}) {
        {{#if voidReturn}}
        return {{inParamName}};
        {{else}}
        return null;
        {{/if}}
    }

    {{/entity.extActions}}
    {{#entity.dataSets}}
    public Page<{{entity.codeName}}> search{{pascalCase codeName}}({{entity.codeName}}SearchContext context) {
        return {{camelCase entity.codeName}}Repository.query(context);
    }
    public List<{{entity.codeName}}> list{{pascalCase codeName}}({{entity.codeName}}SearchContext context) {
        return {{camelCase entity.codeName}}Repository.query(context).getContent();
    }

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

    public boolean removeBy{{pascalCase fkField.codeName}}({{fkField.type.java}} {{camelCase fkField.codeName}}) {
        return false;
    }

    public boolean resetBy{{pascalCase fkField.codeName}}({{fkField.type.java}} {{camelCase fkField.codeName}}) {
        return false;
    }

    public boolean saveBy{{pascalCase 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 && (!getSelf().updateBatch(_update)))
            return false;
        if(_create.size()>0 && (!getSelf().createBatch(_create)))
            return false;
        if(delIds.size()>0 && (!getSelf().removeBatch(delIds)))
            return false;
        return true;
    }

    {{/entity.references}}
    {{#each entity.nesteds}}
        {{#if listCode}}
        {{#unless columnName}}
    public List<{{entityCodeName}}> get{{pascalCase codeName}}({{entity.codeName}} et) {
        return null;
    }

        {{/unless}}
        {{/if}}

    {{/each}}

}