package com.tykj.workflowcore.model.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.tykj.workflowcore.model.entity.Bind;
import com.tykj.workflowcore.model.entity.ColumnInfo;
import com.tykj.workflowcore.model.entity.TableInfo;
import com.tykj.workflowcore.model.entity.TableInfoEx;
import com.tykj.workflowcore.model.repository.BindRepository;
import com.tykj.workflowcore.model.repository.TableInfoExRepository;
import com.tykj.workflowcore.model.repository.TableInfoRepository;
import com.tykj.workflowcore.workflow_editer.service.FormPageService;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static java.util.Objects.nonNull;

@SuppressWarnings("Duplicates")
@Service
public class TableInfoExService {

    @Autowired
    TableInfoExRepository tableInfoExRepository;
    @Autowired
    TableInfoRepository tableInfoRepository;
    @Autowired
    BindRepository bindRepository;
    @Autowired
    TableInfoService tableInfoService;
    @Autowired
    FormPageService formPageService;

    public void save(TableInfoEx tableInfoEx) {
        //数据检查
        boolean exists = tableInfoExRepository.existsByName(tableInfoEx.getName());
        if (exists) {
            throw new RuntimeException("该模型已存在");
        }
        if (nonNull(tableInfoEx.getId())) {
            throw new RuntimeException("新增操作不可附带id");
        }
        //设置时间
        Date date = new Date();
        tableInfoEx.setCreatedTime(date);
        tableInfoEx.setUpdatedTime(date);
        //设置版本号
        tableInfoEx.setVersion(1);
        //保存数据
        Integer id = tableInfoExRepository.save(tableInfoEx).getId();
        if (nonNull(tableInfoEx.getBinds())) {
            tableInfoEx.getBinds().forEach(bind -> save(bind, id));
        }
    }

    public void update(TableInfoEx tableInfoEx) {
        //数据检查
        boolean exists = tableInfoExRepository.existsByName(tableInfoEx.getName());
        if (!exists) {
            throw new RuntimeException("该模型不存在");
        }
        if (nonNull(tableInfoEx.getId())) {
            throw new RuntimeException("修改操作不可附带id");
        }
        //检查是否已在运行中
        formPageService.checkIfWorking(tableInfoEx.getProcessKey());
        //设置时间
        Date date = new Date();
        tableInfoEx.setCreatedTime(date);
        tableInfoEx.setUpdatedTime(date);
        //设置版本号
        Integer count = tableInfoExRepository.countByName(tableInfoEx.getName());
        tableInfoEx.setVersion(count + 1);
        //保存数据
        Integer id = tableInfoExRepository.save(tableInfoEx).getId();
        if (nonNull(tableInfoEx.getBinds())) {
            tableInfoEx.getBinds().forEach(bind -> save(bind, id));
        }
    }

    public List<TableInfoEx> findAll() {
        return tableInfoExRepository.findAll().stream()
                .map(TableInfoEx::getName)
                .distinct()
                .map(this::findLastVersion)
                .map(this::getBinds)
                .collect(Collectors.toList());
    }

    public void deleteByName(String name) {
        List<TableInfoEx> tableInfoExes = tableInfoExRepository.findByName(name);
        for (TableInfoEx tableInfoEx : tableInfoExes) {
            Integer tableInfoExId = tableInfoEx.getId();
            bindRepository.deleteAllByTableInfoExId(tableInfoExId);
            tableInfoExRepository.deleteById(tableInfoExId);
        }
    }

    public List<TableInfoEx> findByProcessKey(String processKey) {
        return tableInfoExRepository.findByProcessKey(processKey).stream()
                .map(TableInfoEx::getName)
                .distinct()
                .map(this::findLastVersion)
                .map(this::getBinds)
                .collect(Collectors.toList());
    }

    public List<Map<String, Map<String, Object>>> findByProcessKeyInMap(String processKey) {
        return tableInfoExRepository.findByProcessKey(processKey).stream()
                .map(TableInfoEx::getName)
                .distinct()
                .map(name -> ImmutableMap.of(name, findByNameInMap(name)))
                .collect(Collectors.toList());
    }

    public String findByNameInJson(String name) {
        Map<String, Object> result = findByNameInMap(name);
        try {
            return new ObjectMapper().writeValueAsString(result);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return "{}";
        }
    }

    public void updateRelatedTableInfoEx(TableInfo tableInfo) {
        List<String> names = bindRepository.findByName(tableInfo.getName()).stream()
                .map(Bind::getTableInfoExId)
                .distinct()
                .map(tableInfoExId -> tableInfoExRepository.findById(tableInfoExId).orElse(null))
                .filter(Objects::nonNull)
                .map(TableInfoEx::getName)
                .distinct()
                .collect(Collectors.toList());
        for (String name : names) {
            TableInfoEx tableInfoEx = tableInfoExRepository.findByName(name).stream()
                    .max(Comparator.comparingInt(TableInfoEx::getVersion))
                    .map(this::getBinds)
                    .orElseThrow(() -> new RuntimeException("计算中出现异常"));
            Integer lastVersion = tableInfoEx.getVersion();
            List<Bind> binds = tableInfoEx.getBinds();
            TableInfoEx newTableInfoEx = new TableInfoEx()
                    .setName(tableInfoEx.getName())
                    .setDescription(tableInfoEx.getDescription())
                    .setProcessKey(tableInfoEx.getProcessKey())
                    .setVersion(lastVersion + 1)
                    .setBinds(newBinds(binds, tableInfo));
            update(newTableInfoEx);
        }
    }

    //-----------------------------------------------------------------------------//

    private Map<String, Object> findByNameInMap(String name) {
        TableInfoEx tableInfoEx = tableInfoExRepository.findByName(name).stream()
                .max(Comparator.comparingInt(TableInfoEx::getVersion))
                .map(this::getBinds)
                .orElseThrow(() -> new RuntimeException("查询数据失败"));
        Map<String, Object> result = new HashMap<>();
        for (Bind bind : tableInfoEx.getBinds()) {
            Map<String, Object> map = bindToMap(bind);
            result.put(bind.getName(), map);
        }
        return result;
    }

    private List<Bind> newBinds(List<Bind> binds, TableInfo tableInfo) {
        List<Bind> newBinds = new ArrayList<>();
        for (Bind bind : binds) {
            Bind newBind = new Bind()
                    .setName(bind.getName())
                    .setVersion(bind.getVersion())
                    .setIsComplex(bind.getIsComplex());
            if (Objects.equals(bind.getName(), tableInfo.getName())) {
                newBind.setVersion(tableInfo.getVersion());
            }
            if (hasChildren(bind)) {
                List<Bind> children = bind.getChildren();
                newBind.setChildren(newBinds(children, tableInfo));
            }
            newBinds.add(newBind);
        }
        return newBinds;
    }

    private boolean hasChildren(Bind bind) {
        return nonNull(bind.getChildren()) && !bind.getChildren().isEmpty();
    }

    private void save(Bind bind, Integer tableInfoExId) {
        Integer lastVersion = tableInfoRepository.countByName(bind.getName());
        bind.setVersion(lastVersion);
        bind.setTableInfoExId(tableInfoExId);
        Integer id = bindRepository.save(bind).getId();
        if (hasChildren(bind)) {
            List<Bind> childrenForSave = bind.getChildren().stream()
                    .map(child -> child.setParentId(id))
                    .collect(Collectors.toList());
            childrenForSave.forEach(child -> save(child, tableInfoExId));
        }
    }

    private TableInfoEx getBinds(TableInfoEx tableInfoEx) {
        List<Bind> binds = bindRepository.findByTableInfoExIdAndParentId(tableInfoEx.getId(), null).stream()
                .map(this::getChildren)
                .map(bind -> bind.setColumnInfos(tableInfoService.findLastVersion(bind.getName()).getColumnInfos()))
                .collect(Collectors.toList());
        tableInfoEx.setBinds(binds);
        return tableInfoEx;
    }

    private Bind getChildren(Bind bind) {
        List<Bind> children = bindRepository.findByTableInfoExIdAndParentId(bind.getTableInfoExId(), bind.getId());
        if (!children.isEmpty()) {
            children = children.stream()
                    .map(this::getChildren)
                    .map(child -> child.setColumnInfos(tableInfoService.findLastVersion(child.getName()).getColumnInfos()))
                    .collect(Collectors.toList());
        }
        bind.setChildren(children);
        return bind;
    }

    private Map<String, Object> bindToMap(Bind bind) {
        Map<String, Object> result = new HashMap<>();
        TableInfo tableInfo = tableInfoService.findByNameAndVersion(bind.getName(), bind.getVersion());
        if (nonNull(tableInfo)) {
            List<ColumnInfo> columnInfos = tableInfo.getColumnInfos();
            for (ColumnInfo columnInfo : columnInfos) {
                result.put(columnInfo.getName(), Strings.EMPTY);
            }
            if (hasChildren(bind)) {
                for (Bind child : bind.getChildren()) {
                    Map<String, Object> childMap = bindToMap(child);
                    result.put(child.getName(), childMap);
                }
            }
        }
        return result;
    }

    private TableInfoEx findLastVersion(String name) {
        return tableInfoExRepository.findByName(name).stream()
                .max(Comparator.comparingInt(TableInfoEx::getVersion))
                .orElseThrow(() -> new RuntimeException("查询失败"));
    }
}
