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

import cn.ibizlab.core.lite.domain.DstApp;
import cn.ibizlab.core.lite.domain.DstSystem;
import cn.ibizlab.core.lite.domain.MetaEntity;
import cn.ibizlab.core.lite.domain.MetaModel;
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.MetaFieldSearchContext;
import cn.ibizlab.core.lite.service.*;
import cn.ibizlab.util.domain.FileItem;
import cn.ibizlab.util.errors.InternalServerErrorException;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.*;

import static cn.ibizlab.util.service.SimpleFileService.getExtensionName;

@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 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);

        Map<String, RelationshipModel> parentSet = new LinkedHashMap();
        List<RelationshipModel> references = new ArrayList<>();
        if (entity != null) {
            entityModel.setDataSets(metaDataSetService.selectByEntityId(entity.getEntityId()));
            metaRelationshipService.selectByEntityId(entity.getEntityId()).forEach(item->
            {
                RelationshipModel model = new RelationshipModel();
                MetaEntity parentEntity = LiteStorage.getMetaEntity(item.getRefEntityId());
                if(parentEntity!=null){
                    model.setRelation(item);
                    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.setRelation(item);
                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(LiteStorage::putMetaEntity);
    }

    @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;

    @Value("${ibiz.filePath:/app/file/}")
    private String fileRoot;

    @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;
    }

    public FileItem uploadFile(MultipartFile multipartFile) {
        FileItem item;
        // 获取文件名
        String fileName = multipartFile.getOriginalFilename();
        // 获取文件后缀
        String extname = "."+getExtensionName(fileName);
        try {
            String fileid = DigestUtils.md5DigestAsHex(multipartFile.getInputStream());
            String fileFullPath = this.fileRoot+"ibizutil"+File.separator+File.separator+fileName;
            File file = new File(fileFullPath);
            File parent = new File(file.getParent());
            if(!parent.exists()) {
                parent.mkdirs();
            }
            FileCopyUtils.copy(multipartFile.getInputStream(), Files.newOutputStream(file.toPath()));
            item = new FileItem(fileid, fileName, fileid, fileName, (int)multipartFile.getSize(), extname);
        } catch (IOException e) {
            throw new InternalServerErrorException("文件上传失败，"+e);
        }
        return item;
    }



    public String saveFile(String jsonObject) {
        // 获取文件名
        String fileName = "packageModel_"+UUID.randomUUID().toString().replace("-","").toUpperCase();
        // 获取文件后缀
        String extname = ".json";
        try {
            String fileFullPath = this.fileRoot+"ibizutil"+File.separator+fileName+extname;
            File file = new File(fileFullPath);
            File parent = new File(file.getParent());
            if(!parent.exists()) {
                parent.mkdirs();
            }
            writeFile(fileFullPath,jsonObject);
        } catch (Exception e) {
            throw new InternalServerErrorException("文件上传失败，"+e);
        }
        return fileName+extname;
    }

    public File getFile(String fileId) {
        String dirpath = this.fileRoot + "ibizutil" + File.separator + fileId;
        File parent = new File(dirpath);
        if (parent.exists()) {
            return parent;
        }
        throw new InternalServerErrorException("文件未找到");
    }
    public static void writeFile(String filePath, String sets) throws IOException {
        FileWriter fw = new FileWriter(filePath);
        PrintWriter out = new PrintWriter(fw);
        out.write(sets);
        out.println();
        fw.close();
        out.close();
    }


}
