package cn.lihongjie.coal.dictionary.service;

import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.dto.TreeDto;
import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.common.TreeUtils;
import cn.lihongjie.coal.dictionary.dto.*;
import cn.lihongjie.coal.dictionary.entity.DictionaryEntity;
import cn.lihongjie.coal.dictionary.entity.DictionaryItemEntity;
import cn.lihongjie.coal.dictionary.mapper.DictionaryItemMapper;
import cn.lihongjie.coal.dictionary.mapper.DictionaryMapper;
import cn.lihongjie.coal.dictionary.repository.DictionaryRepository;
import cn.lihongjie.coal.exception.BizException;
import cn.lihongjie.coal.script.dto.ScriptExecResultDto;
import cn.lihongjie.coal.script.entity.ScriptEntity;
import cn.lihongjie.coal.script.service.ScriptService;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import jakarta.annotation.PostConstruct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@Slf4j
@Transactional
public
class DictionaryService extends BaseService<DictionaryEntity, DictionaryRepository> {

    @Autowired DictionaryRepository repository;

    @Autowired DictionaryMapper mapper;
    @Autowired ScriptService scriptService;
    @Autowired ConversionService conversionService;
    @Autowired DictionaryItemMapper dictionaryItemMapper;

    @PostConstruct
    public void init() {}

    @SneakyThrows
    public DictionaryTreeDto tree(DictTreeRequest request) {

        DictionaryEntity dict = this.repository.findByCode(request.getCode());
        if (dict == null) {
            throw new BizException("数据字典 %s 不存在".formatted(request.getCode()));
        }
        boolean flatten = StringUtils.equalsIgnoreCase(dict.getComponentType(), "1");
        DictionaryTreeDto ans = this.mapper.toDictTree(dict);
        if (dict.getDictType().equalsIgnoreCase("1")) {

            ans.setTree(TreeDto.buildList(dict.getItem(), flatten));
        } else if (dict.getDictType().equalsIgnoreCase("2")) {

            ScriptExecResultDto resultDto = new ScriptExecResultDto();
            resultDto.setId(dict.getScript().getId());
            resultDto.setParams(request.getParams());
            ScriptExecResultDto result = scriptService.exec(resultDto);
            if (StringUtils.isNotEmpty(result.getStackTrace())) {
                log.warn(
                        "执行脚本出错 id:{}\nparams:{}\nstacktrace:{}\nlogs:{}",
                        result.getId(),
                        result.getParams() + "",
                        result.getStackTrace(),
                        result.getLogs());
            }
            ans.setTree(TreeDto.buildList(result.getResponse(), flatten));
        } else {

            throw new BizException("不支持的字典类型 " + dict.getDictTypeName());
        }

        return ans;
    }

    public DictionaryDto create(CreateDictionaryDto request) {

        DictionaryEntity entity = mapper.toEntity(request);

        this.repository.save(entity);
        return getById(entity.getId());
    }

    public DictionaryDto update(UpdateDictionaryDto request) {
        DictionaryEntity entity = this.repository.get(request.getId());
        this.mapper.updateEntity(entity, request);

        if (entity.getItem() != null) {
            entity.getItem().forEach(x -> x.setDictionary(entity));
        }
        this.repository.save(entity);

        return getById(entity.getId());
    }

    public void delete(IdRequest request) {

        this.repository.deleteAllById(request.getIds());
    }

    public DictionaryDto getById(String id) {

        DictionaryEntity entity = repository.get(id);

        return mapper.toDto(entity);
    }

    public Page<DictionaryDto> list(CommonQuery query) {

        Page<DictionaryEntity> page =
                repository.findAll(
                        query.specification(conversionService),
                        PageRequest.of(
                                query.getPageNo(),
                                query.getPageSize(),
                                Sort.by(query.getOrders())));

        return page.map(this.mapper::toDto);
    }

    public DictionaryDetailedDto getByIdDetailed(String id) {
        DictionaryEntity entity = repository.get(id);

        return mapper.toDetailedDto(entity);
    }

    @SneakyThrows
    public void initDefault() {

        ClassPathResource classPathResource = new ClassPathResource("/config/dictionary.json");

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        List<DictionaryEntity> defaultDicts;

        List<DictionaryEntity> exists = findAll();

        try (InputStream inputStream = classPathResource.getInputStream()) {
            defaultDicts =
                    mapper.readValue(inputStream, new TypeReference<List<DictionaryEntity>>() {});
        }

        for (DictionaryEntity dictionary : defaultDicts) {

            boolean found = false;
            for (DictionaryEntity exist : exists) {

                if (StringUtils.equalsIgnoreCase(dictionary.getCode(), exist.getCode())) {

                    for (DictionaryItemEntity item : dictionary.getItem()) {
                        found = true;
                        exist.addItem(dictionaryItemMapper.copy(item));
                    }
                }
            }

            if (!found) {
                DictionaryEntity nd = new DictionaryEntity();
                nd.setCode(dictionary.getCode());
                nd.setName(dictionary.getName());

                nd.setDictType("1");
                nd.setComponentType(StringUtils.defaultIfBlank(dictionary.getComponentType(), "1"));

                for (DictionaryItemEntity item : dictionary.getItem()) {
                    nd.addItem(item);
                }

                this.repository.save(nd);
            }
        }
    }

    public void createScriptDict(ScriptEntity scriptEntity) {

        Optional<DictionaryEntity> byScript = this.repository.findByScript(scriptEntity);

        if (byScript.isPresent()) {
        } else {

            DictionaryEntity dictionary = new DictionaryEntity();

            dictionary.setName(scriptEntity.getName());
            dictionary.setCode(scriptEntity.getCode());

            dictionary.setDictType("2");
            dictionary.setComponentType(scriptEntity.getCode().contains("tree") ? "2" : "1");

            dictionary.setScript(scriptEntity);
            dictionary.setStatus(1);

            this.repository.save(dictionary);
        }
    }


    @PersistenceContext
    private EntityManager entityManager;

    public List<TreeDto> autoComplete(AutoCompleteRequest request){

            Query nativeQuery;
        if (StringUtils.isNotEmpty(request.getOrganizationId())){

            nativeQuery = entityManager.createNativeQuery(
                    "select distinct " +
                    request.getFieldName() +
                    "  from " +
                    request.getTableName() +
                    " where  " +
                    " organization_id = '" +
                    request.getOrganizationId() +
                    "'" +
                    " and  " +
                    request.getFieldName() +
                    " like '%" +
                    ObjectUtils.defaultIfNull(request.getQuery(), "") +
                    "%'");

        }else {

            nativeQuery = entityManager.createNativeQuery(

                    "select distinct " +
                    request.getFieldName() +
                    "  from " +
                    request.getTableName() +
                    " where  " +
                    request.getFieldName() +
                    " like '%" +
                    ObjectUtils.defaultIfNull(request.getQuery(), "") +
                    "%'");


        }

        return nativeQuery.getResultList().stream().map(x -> new TreeDto(null, null, x.toString(), null)).toList();



    }

    public List<String> getExcelValidation(String s) {


        DictTreeRequest request = new DictTreeRequest();
        request.setCode(s);
        DictionaryTreeDto tree = tree(request);

        ArrayList<String> list = new ArrayList<>();

        TreeUtils.dfsList(tree.getTree(), (TreeDto x )->x.getChildren(), object -> list.add(object.getCurrent().getCode() + "-" + object.getCurrent().getName()), (TreeDto x )->x.getId());

        return list;
    }
}
