package cn.ibizlab.core.lite.extensions.service;

import cn.ibizlab.core.lite.domain.*;
import cn.ibizlab.core.lite.extensions.domain.EntityModel;
import cn.ibizlab.core.lite.extensions.domain.FieldModel;
import cn.ibizlab.core.lite.extensions.domain.RelationshipModel;
import cn.ibizlab.core.lite.extensions.model.DataModel;
import cn.ibizlab.core.lite.extensions.util.LiteStorage;
import cn.ibizlab.core.lite.filter.MetaEntitySearchContext;
import cn.ibizlab.core.lite.filter.MetaFieldSearchContext;
import cn.ibizlab.core.lite.service.*;
import cn.ibizlab.util.errors.BadRequestAlertException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.PostConstruct;
import java.sql.Wrapper;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
public class LiteModelService {

    @PostConstruct
    public void init(){
        LiteStorage.setLiteModelService(this);
    }

    @Autowired
    @Lazy
    private IMetaEntityService metaEntityService;

    public IMetaEntityService getMetaEntityService()
    {
        return metaEntityService;
    }

    @Autowired
    @Lazy
    private IMetaFieldService metaFieldService;

    @Autowired
    @Lazy
    private IMetaRelationshipService metaRelationshipService;


    @Autowired
    @Lazy
    private IDstDataSourceService dstDataSourceService;

    @Autowired
    @Lazy
    private IMetaDataSetService metaDataSetService;

    @Autowired
    @Lazy
    private LiteModelService proxyService;

    public List<EntityModel> getEntityModel(String systemId)
    {
        List<EntityModel> list = new ArrayList<>();
        metaEntityService.list(Wrappers.<MetaEntity>lambdaQuery().eq(MetaEntity::getSystemId,systemId).orderByAsc(MetaEntity::getEntityName)).forEach(entity -> {
            EntityModel entityModel = new EntityModel();
            entityModel.setEntity(entity);
            list.add(entityModel);
        });
        return list;
    }

    public EntityModel getProxyEntityModel(String systemId, String name)
    {
        return proxyService.getEntityModel(systemId,name);
    }

    @Cacheable( value="entitymodel",key = "'row:'+#p0+'.'+#p1")
    public EntityModel getEntityModel(String systemId, String name)
    {
        MetaEntity entity = LiteStorage.getMetaEntity(String.format("%1$s.%2$s",systemId,name));

        EntityModel entityModel = new EntityModel();
        entityModel.setEntity(entity);
        entityModel.setDataSets(metaDataSetService.selectByEntityId(entity.getEntityId()));

        Map<String, RelationshipModel> parentSet = new LinkedHashMap();
        List<RelationshipModel> references = new ArrayList<>();
        metaRelationshipService.selectByEntityId(entity.getEntityId()).forEach(item->
        {
            RelationshipModel model = new RelationshipModel();
            MetaEntity parentEntity = LiteStorage.getMetaEntity(item.getRefEntityId());
            if(parentEntity!=null){
                model.setCodeName(item.getCodeName());
                model.setDataSourceName(parentEntity.getDsName());
                model.setEntityCodeName(parentEntity.getCodeName());
                model.setEntityId(parentEntity.getEntityId());
                model.setEntityLogicName(parentEntity.getLogicName());
                model.setEntityName(parentEntity.getEntityName());
                model.setSystemId(parentEntity.getSystemId());
                model.setTableName(parentEntity.getTableName());
                parentSet.put(item.getId(),model);
                references.add(model);
            }
        });
        entityModel.setReferences(references);

        List<RelationshipModel> nesteds = new ArrayList<>();
        metaRelationshipService.selectByRefEntityId(entity.getEntityId()).forEach(item->
        {
            RelationshipModel model = new RelationshipModel();
            MetaEntity subEntity = LiteStorage.getMetaEntity(item.getEntityId());
            if(subEntity!=null){
                model.setCodeName(StringUtils.isEmpty(item.getNestedName())?item.getCodeName()+"_"+item.getEntityName():item.getNestedName());
                model.setDataSourceName(subEntity.getDsName());
                model.setEntityCodeName(subEntity.getCodeName());
                model.setEntityId(subEntity.getEntityId());
                model.setEntityLogicName(subEntity.getLogicName());
                model.setEntityName(subEntity.getEntityName());
                model.setSystemId(subEntity.getSystemId());
                model.setTableName(subEntity.getTableName());
                nesteds.add(model);
            }
        });
        entityModel.setNesteds(nesteds);

        List<FieldModel> fields = new ArrayList<>();
        MetaFieldSearchContext searchContext = new MetaFieldSearchContext();
        searchContext.setN_entityid_eq(entity.getEntityId());
        searchContext.getSelectCond().orderByAsc("showorder");
        searchContext.setSize(Integer.MAX_VALUE);
        metaFieldService.searchDefault(searchContext).getContent().forEach(item->
        {
            FieldModel model = new FieldModel();
            model.setField(item);
            model.setCodeName(item.getCodeName());
            model.setColumnName(item.getFieldName());
            model.setComment(item.getFieldLogicName());
            model.setShowName(item.getFieldShowName());
            model.setUnionName(item.getFieldUniName());
            if((!StringUtils.isEmpty(item.getRelationId()))&&(parentSet.containsKey(item.getRelationId())))
                model.setReference(parentSet.get(item.getRelationId()));
            fields.add(model);
        });
        entityModel.setFields(fields);


        return entityModel;
    }

    @CacheEvict( value="entitymodel",key = "'row:'+#p0+'.'+#p1")
    public void resetEntityModel(String systemId, String name)
    {
        LiteStorage.resetMetaEntity(String.format("%1$s.%2$s",systemId,name));
    }


    public void initMetaEntity()
    {
        metaEntityService.list().forEach(metaEntity -> {
            LiteStorage.putMetaEntity(metaEntity);
        });
    }

    @Autowired
    @Lazy
    private IMetaModelService metaModelService;

    public DataModel getDataModel(String dataModelId)
    {
        String cfg=metaModelService.get(dataModelId).getConfig();
        DataModel dataModel =  JSON.toJavaObject(JSON.parseObject(metaModelService.get(dataModelId).getConfig()),DataModel.class);
        dataModel.setModelString(cfg);
        return dataModel;
    }

    public DataModel getDataModel(String rootDataModelId,String subDataModelName)
    {
        DataModel dataModel=getDataModel(rootDataModelId);
        if(StringUtils.isEmpty(subDataModelName))
            return dataModel;
        return dataModel.findDataModel(subDataModelName,"ALL");
    }

    @Autowired
    @Lazy
    private IDstSystemService dstSystemService;


    @Cacheable( value="syspssystem",key = "'row:all-dst-apps'")
    public LinkedHashMap<String, DstApp> getApps() {
        LinkedHashMap<String,DstApp> appNode=new LinkedHashMap<>();
        dstSystemService.list().forEach(system -> {
            if (system.getSysstructure() == null)
            {
                return;
            }
            if (system.getApps() == null)
            {
                system.setApps(system.getSysstructure().getSysApps(true));
                dstSystemService.update(system);
            }
            system.getApps().forEach(app-> {
                appNode.put(app.getId(),app);
            });
        });
        return appNode;
    }

    @Caching( evict = {
            @CacheEvict( value="syspssystem",key = "'row:all-apps'"),
            @CacheEvict( value="syspssystem",key = "'row:all-dst-apps'"),
            @CacheEvict( value="syspssystem",key = "'row:all-dst-sys-apps'")
    })
    public void resetApps()
    {

    }


    @Cacheable( value="syspssystem",key = "'row:sys-dst-sys-apps'")
    public List<DstSystem> getSysApp()
    {
        List<DstSystem> list = new ArrayList<>();
        dstSystemService.list().forEach(system -> {
            if (system.getSysstructure() == null)
            {
                return;
            }
            if (system.getApps() == null)
            {
                system.setApps(system.getSysstructure().getSysApps(true));

            }
            system.setSysstructure(null);
            list.add(system);
        });
        return list;
    }

}
