package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.analysis.domain.DABuild;
import cn.ibizlab.core.analysis.domain.DADimension;
import cn.ibizlab.core.analysis.domain.DAMetric;
import cn.ibizlab.core.analysis.filter.DABuildSearchContext;
import cn.ibizlab.core.analysis.filter.DADimensionSearchContext;
import cn.ibizlab.core.analysis.filter.DAMetricSearchContext;
import cn.ibizlab.core.analysis.service.IDABuildService;
import cn.ibizlab.core.analysis.service.IDADimensionService;
import cn.ibizlab.core.analysis.service.IDAMetricService;
import cn.ibizlab.core.extensions.domain.AssembleModel;
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.model.Property;
import cn.ibizlab.core.lite.extensions.service.LiteModelService;
import cn.ibizlab.core.lite.filter.DstDataSourceSearchContext;
import cn.ibizlab.core.lite.filter.MetaFieldSearchContext;
import cn.ibizlab.core.lite.service.*;
import cn.ibizlab.core.lite.service.impl.MetaModelServiceImpl;
import cn.ibizlab.core.rule.domain.RuleItem;
import cn.ibizlab.core.rule.service.IRuleItemService;
import cn.ibizlab.util.errors.BadRequestAlertException;
import cn.ibizlab.util.errors.InternalServerErrorException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Joiner;
import liquibase.pro.packaged.D;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 实体[模型] 自定义服务对象
 */
@Slf4j
@Primary
@Service("MetaModelExService")
public class MetaModelExService extends MetaModelServiceImpl {
    @Resource
    @Lazy
    private IMetaModelService metaModelService;

    @Resource
    @Lazy
    public IRuleItemService ruleItemService;

    @Resource
    @Lazy
    public IDABuildService idaBuildService;

    @Resource
    @Lazy
    public IDAMetricService idaMetricService;

    @Resource
    @Lazy
    public IDADimensionService idaDimensionService;

    @Resource
    @Lazy
    private LiteModelService liteModelService;

    @Resource
    @Lazy
    private IMetaEntityService metaEntityService;

    @Resource
    @Lazy
    private IDstDataSourceService dstDataSourceService;

    @Autowired
    @Lazy
    private IMetaDataSetService dataSetService;

    @Autowired
    @Lazy
    private IMetaRelationshipService relationshipService;

    @Autowired
    @Lazy
    private IMetaFieldService fieldService;

    @Autowired
    @Lazy
    private IDstSystemService dstSystemService;

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

    @Override
    protected Class currentModelClass() {
        return com.baomidou.mybatisplus.core.toolkit.ReflectionKit.getSuperClassGenericType(this.getClass().getSuperclass(), 1);
    }

    @Override
    @Transactional
    public MetaModel change(MetaModel et) {
        return super.change(et);
    }

    /**
     * [ExportFile:导出文件] 行为扩展：导出文件json格式
     *
     * @param et
     * @return
     */
    @Override
    @Transactional
    public MetaModel exportFile(MetaModel et) {
        return super.exportFile(et);
    }

    /**
     * [ImportFile:导入文件] 行为扩展：传入一个文件，将该文件转化成对象，根据对象执行表更新操作
     *
     * @param et
     * @return
     */
    @Override
    @Transactional
    public MetaModel importFile(MetaModel et) {
        JSONArray fileArray = JSONArray.parseArray(et.getName());
        for (int a = 0; a < fileArray.size(); a++) {
            JSONObject objFile = fileArray.getJSONObject(a);
            String strModel = getModel(objFile.getString("id"));
            List<AssembleModel> models = JSONObject.parseArray(strModel, AssembleModel.class);
            for (AssembleModel model : models) {
                //基础数据导入
                if (model.getEntityModels() != null) {
                    LinkedHashSet<MetaEntity> entities = new LinkedHashSet<>();
                    LinkedHashSet<MetaRelationship> p_relations = new LinkedHashSet<>();
                    LinkedHashSet<MetaRelationship> s_relations = new LinkedHashSet<>();
                    LinkedHashSet<MetaField> fields = new LinkedHashSet<>();
                    LinkedHashSet<MetaDataSet> dataSets = new LinkedHashSet<>();
                    model.getEntityModels().forEach(item -> {
                        if(!ObjectUtils.isEmpty(item.getReferences())){
                            for(RelationshipModel relationModel : item.getReferences()){
                                if(!ObjectUtils.isEmpty(relationModel.getRelation())){
                                    p_relations.add(relationModel.getRelation());
                                }
                            }
                        }
                        if(!ObjectUtils.isEmpty(item.getNesteds())){
                            for(RelationshipModel relationModel : item.getNesteds()){
                                if(!ObjectUtils.isEmpty(relationModel.getRelation())){
                                    s_relations.add(relationModel.getRelation());
                                }
                            }
                        }
                        if(!ObjectUtils.isEmpty(item.getFields())){
                            List<FieldModel> fieldModels = item.getFields();
                            for(FieldModel fieldModel : fieldModels){
                                if(!ObjectUtils.isEmpty(fieldModel.getField())){
                                    fields.add(fieldModel.getField());
                                }
                            }
                        }
                        if(!ObjectUtils.isEmpty(item.getDataSets())){
                            dataSets.addAll(item.getDataSets());
                        }
                        entities.add(item.getEntity());
                    });
                    //核心数据导入
                    if (model.getMetaModel() != null) {
                        metaModelService.save(model.getMetaModel());
                    }
                    if (model.getEntityModels().size() > 0) {
                        metaEntityService.saveBatch(entities);
                    }
                    if (model.getSystems().size() > 0) {
                        dstSystemService.saveBatch(model.getSystems());
                    }
                    if (model.getDataSources().size() > 0) {
                        dstDataSourceService.saveBatch(model.getDataSources());
                    }
                    if (model.getDaBuilds().size() > 0) {
                        idaBuildService.saveBatch(model.getDaBuilds());
                    }
                    if (model.getMetrics().size() > 0) {
                        idaMetricService.saveBatch(model.getMetrics());
                    }
                    if (model.getRuleItems().size() > 0) {
                        ruleItemService.saveBatch(model.getRuleItems());
                    }
                    if(p_relations.size()>0){
                        relationshipService.saveBatch(p_relations);
                    }
                    if(s_relations.size()>0){
                        relationshipService.saveBatch(s_relations);
                    }
                    if(dataSets.size()>0){
                        dataSetService.saveBatch(dataSets);
                    }
                    if(fields.size()>0){
                        fieldService.saveBatch(fields);
                    }
                }
            }
        }
        return super.importFile(et);
    }

    /**
     * 批量导出数据到json文件中
     *
     * @param metaModels
     * @return
     */
    @Override
    @Transactional
    public boolean exportFileBatch(List<MetaModel> metaModels) {
        Object req = RequestContextHolder.getRequestAttributes();
        if (ObjectUtils.isEmpty(req) || !(req instanceof ServletRequestAttributes)) {
            new BadRequestAlertException("模型导出失败", "", "");
        }
        List<AssembleModel> models = new ArrayList<>();
        for (MetaModel metaModel : metaModels) {
            models.add(getModel(metaModel));
        }
        String strModel = JSON.toJSONString(models);
        File modelFile = generateModelFile(strModel);
        HttpServletResponse resp = ((ServletRequestAttributes) req).getResponse();
        if (resp != null){
            resp.setContentType("APPLICATION/OCTET-STREAM");//返回格式为流
            resp.setHeader("Content-Disposition", "attachment;filename=" + getFileName(modelFile.getName()));
            sendResponse(resp, modelFile);
        }
        return true;
    }

    /**
     * 把模型所有需要的东西组装后打包导出
     *
     * @param et 所需打包的模型
     * @return
     */
    @Transactional
    public AssembleModel getModel(MetaModel et) {
        String modelId = et.getId();
        AssembleModel models = new AssembleModel();
        List<DAMetric> metrics = new ArrayList<>();
        Map<String, DstSystem> systemMap = new HashMap();
        LinkedHashSet<String> entities = new LinkedHashSet<>();
        List<DstDataSource> dataSources = new ArrayList<>();

        //  查询模型
        MetaModel metaModel = metaModelService.get(modelId);
        //  查询模型下的N个规则
        List<RuleItem> ruleItems = ruleItemService.selectByModelId(modelId);
        if(!ObjectUtils.isEmpty(ruleItems)){
            for (RuleItem ruleItem : ruleItems) {
                JSONObject fieldSet = JSONObject.parseObject(ruleItem.getFieldSet());
                if (!ObjectUtils.isEmpty(fieldSet)) {
                    for (String entityName : fieldSet.keySet()) {
                        if (entityName.endsWith("entity")) {
                            entities.add(fieldSet.getString(entityName));
                        }
                    }
                }
            }
        }
        // 查询模型下的N个构建
        List<DABuild> daBuilds = dabuildService.selectByModelId(modelId);
        List<String> daBuildIds = daBuilds.stream().map(DABuild::getBuildId).collect(Collectors.toList());

        // 查询构建下关联的N个指标
        DAMetricSearchContext damSearchContext = new DAMetricSearchContext();
        damSearchContext.setN_buildid_in(Joiner.on(";").join(daBuildIds));
        Page<DAMetric> daMetrics = idaMetricService.searchDefault(damSearchContext);

        // 添加指标
        if (!ObjectUtils.isEmpty(daMetrics.getContent())) {
            metrics.addAll(daMetrics.getContent());
        }
        // 用到的数据源和系统
        List<EntityModel> entityModels = getEntityModel(modelId);
        LinkedHashSet masterDsName = new LinkedHashSet();
        for (EntityModel entityModel : entityModels) {
            masterDsName.add(entityModel.getDsName());
            if (!ObjectUtils.isEmpty(entityModel.getEntity())) {
                systemMap.put(entityModel.getEntity().getSystem().getPssystemid(), entityModel.getEntity().getSystem());
            }
        }

        // 查询数据源
        if(masterDsName.size() > 0){
            DstDataSourceSearchContext dstDataSourceSearchContext = new DstDataSourceSearchContext();
            dstDataSourceSearchContext.setN_dsid_in(Joiner.on(";").join(masterDsName));
            dstDataSourceSearchContext.setSize(entityModels.size());
            Page<DstDataSource> dstDataSources = dstDataSourceService.searchDefault(dstDataSourceSearchContext);
            dataSources.addAll(dstDataSources.getContent());
        }
        models.setMetrics(metrics);
        models.setMetaModel(metaModel);
        models.setEntityModels(entityModels);
        models.setDaBuilds(daBuilds);
        models.setRuleItems(ruleItems);
        models.setDataSources(dataSources);
        models.setSystems(new ArrayList<>(systemMap.values()));
        return models;
    }

    /**
     * 传入模型id 返回一组实体模型集合
     * @param modelId  模型id
     * @return 实体模型集合
     */
    private List<EntityModel> getEntityModel(String modelId) {
        List<EntityModel> entityModels = new ArrayList<>();
        DataModel dataModel = liteModelService.getDataModel(modelId);

        // 查询出模型父子级用到的实体关系
        dataModel.getAllProperty().forEach(property -> {
            EntityModel model = liteModelService.getEntityModel(property.getSystem(),property.getPropertyEntity());
            entityModels.add(model);
        });
        List<Property> parents = dataModel.getParentProperty();
        LinkedHashSet<DataModel> childs = dataModel.getNestedDataModels();
        for(Property parent : parents){
            EntityModel model = parent.getEntityModel();
            entityModels.add(model);
        }
        for(DataModel child : childs){
            EntityModel model = child.getFactEntityModel();
            entityModels.add(model);
        }
        return entityModels;
    }

    /**
     * 格式化文件名给请求识别
     *
     * @param fileName
     * @return
     */
    protected String getFileName(String fileName) {
        try {
            // 防止中文乱码
            return new String(fileName.getBytes(StandardCharsets.UTF_8), "iso8859-1");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return fileName;
    }

    /**
     * 保存文件在本地后返回一个文件名
     *
     * @param strModel
     * @return
     */
    public File generateModelFile(String strModel) {
        File file;
        Date now = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
        String fileId = DigestUtils.md5DigestAsHex(strModel.getBytes());
        String fileName = format.format(now) + ".json";
        FileWriter fw = null;
        PrintWriter out = null;
        try {
            String fileFullPath = this.fileRoot + "ibizutil" + File.separator + fileId + File.separator + fileName;
            file = new File(fileFullPath);
            File parent = new File(file.getParent());
            if (!parent.exists()) {
                parent.mkdirs();
            }
            fw = new FileWriter(fileFullPath);
            out = new PrintWriter(fw);
            out.write(strModel);
            out.println();
        } catch (Exception e) {
            throw new BadRequestAlertException("生成模型文件失败，" + e, "", "");
        } finally {
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                out.close();
            }
        }
        return file;
    }

    /**
     * 去磁盘读取文件，并将其转化成组装模型对象
     *
     * @param fileId
     * @return
     */
    public String getModel(String fileId) {
        File ModelFile;
        StringBuffer strModel = new StringBuffer();
        try {
            String dirPath = this.fileRoot + "ibizutil" + File.separator + fileId;
            File parent = new File(dirPath);
            if (parent.exists() && parent.isDirectory() && parent.listFiles().length > 0) {
                ModelFile = parent.listFiles()[0];
            } else {
                throw new InternalServerErrorException("文件未找到");
            }
            Reader reader = new InputStreamReader(new FileInputStream(ModelFile), StandardCharsets.UTF_8);
            int ch;
            while ((ch = reader.read()) != -1) {
                strModel.append((char) ch);
            }
            reader.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
        return strModel.toString();
    }

    /**
     * 发送文件响应给浏览器
     *
     * @param response
     * @param file
     */
    protected void sendResponse(HttpServletResponse response, File file) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(file));
            bos = new BufferedOutputStream(response.getOutputStream());
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
