package com.search.supersearch.controller.docs;

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.CountRequest;
import co.elastic.clients.elasticsearch.core.CountResponse;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import com.google.common.collect.Lists;
import com.search.supersearch.common.R;
import com.search.supersearch.domain.IamDataCategory;
import com.search.supersearch.domain.IamSearchCategoryidIndexname;
import com.search.supersearch.model.dto.IamCategoryIndexBean;
import com.search.supersearch.model.dto.SearchDTO;
import com.search.supersearch.model.dto.SearchByCommonIdDTO;
import com.search.supersearch.model.vo.DocsIndexCountTreeVO;
import com.search.supersearch.model.vo.DocsIndexVO;
import com.search.supersearch.service.DocsService;
import io.swagger.annotations.Api;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/docs/v3")
@Slf4j
@Api(value = "文档搜索", tags = "文档搜索")
public class DocsV3Controller {

    @Value("${authority.url}")
    private String url;
    @Resource
    private ElasticsearchClient elasticsearchClient;
    @Resource
    private DocsService docsService;

    @PostMapping("/search")
    @SneakyThrows
    public R<DocsIndexVO> searchV2(@RequestBody SearchDTO dto, HttpServletRequest request) {
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchV2 开始执行={}", System.currentTimeMillis());

        String keyword = dto.getKeyword();
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isEmpty(authorization)) {
            return R.fail("token为空!");
        }
        if (StringUtils.isEmpty(dto.getSearchType())) {
            return R.fail("参数为空!");
        }

        if (dto.getSize() == null) {
            dto.setSize(3);
        }
        List<String> index = new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchV2 开始请求权限计算={}", System.currentTimeMillis());
        Object invokePermission = invokePermission(dto.getSearchType(), authorization);
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchV2 请求权限计算结束={}", System.currentTimeMillis());

        if (invokePermission instanceof String) {
            R fail = R.fail("请求权限接口失败!");
            fail.setExt((String) invokePermission);
            return fail;
        }
        List<IamCategoryIndexBean> list = (List<IamCategoryIndexBean>) invokePermission;
        if (list.size() < 1) {
            return R.fail("该用户没有任何权限!");
        }
        for (IamCategoryIndexBean iamCategoryIndexBean : list) {
            index = iamCategoryIndexBean.getIndexBeanList().stream().map(IamSearchCategoryidIndexname::getIndexName).collect(Collectors.toList());
            map = iamCategoryIndexBean.getIndexBeanList().stream().collect(Collectors.toMap(IamSearchCategoryidIndexname::getIndexName, i -> Optional.ofNullable(i.getOrgCodes()).orElse(Lists.newArrayList())));

            categoryAllIndex(index, map, iamCategoryIndexBean);
        }

        SearchResponse<Map> search = docsService.searchHitv2(keyword, dto.getMatch(), dto.getSort(), index, dto.getSize(), dto.getSearchAfter(), map);
        if (search == null) {
            return R.data(new DocsIndexVO());
        }
        DocsIndexVO vo = getDocsIndexVO(search.hits(), "", "");
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchV2 执行结束={}", System.currentTimeMillis());
        return R.data(vo);
    }

    @PostMapping("/searchAll")
    @SneakyThrows
    public R<List<DocsIndexVO>> searchAllV2(@RequestBody SearchDTO dto, HttpServletRequest request) {
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchAllV2 开始执行={}", System.currentTimeMillis());

        String keyword = dto.getKeyword();

        if (dto.getSize() == null) {
            dto.setSize(3);
        }
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isEmpty(authorization)) {
            return R.fail("token为空!");
        }
        ArrayList<DocsIndexVO> vos = Lists.newArrayList();
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchAllV2 开始请求权限计算={}", System.currentTimeMillis());
        Object invokePermission = invokePermission("", authorization);
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchAllV2 请求权限计算结束={}", System.currentTimeMillis());
        if (invokePermission instanceof String) {
            R fail = R.fail("请求权限接口失败!");
            fail.setExt((String) invokePermission);
            return fail;
        }
        List<IamCategoryIndexBean> category = (List<IamCategoryIndexBean>) invokePermission;

        if (category.size() < 1) {
            return R.fail("该用户没有任何权限!");
        }
        List<String> hideType = dto.getHideType();

        for (IamCategoryIndexBean iamDataCategory : category) {

            IamDataCategory dataCategoryBean = iamDataCategory.getDataCategoryBean();
            if (hideType != null && hideType.stream().anyMatch(v -> v.equalsIgnoreCase(dataCategoryBean.getCategoryId()))) {
                continue;
            }
            List<String> index = iamDataCategory.getIndexBeanList().stream().map(IamSearchCategoryidIndexname::getIndexName).collect(Collectors.toList());
            Map<String, List<String>> esOrdId_map = iamDataCategory.getIndexBeanList().stream().collect(Collectors.toMap(IamSearchCategoryidIndexname::getIndexName, i -> Optional.ofNullable(i.getOrgCodes()).orElse(Lists.newArrayList())));

            categoryAllIndex(index, esOrdId_map, iamDataCategory);
            SearchResponse<Map> search = docsService.searchHitv2(keyword, dto.getMatch(), dto.getSort(), index, dto.getSize(), dto.getSearchAfter(), esOrdId_map);
            if (search == null) {
                continue;
            }
            HitsMetadata<Map> hits = search.hits();
            if (hits.total() == null || hits.total().value() == 0) {
                continue;
            }
            DocsIndexVO vo = getDocsIndexVO(hits, dataCategoryBean.getCategoryId(), dataCategoryBean.getCategoryName());
            vos.add(vo);
        }
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchAllV2 执行结束={}", System.currentTimeMillis());

        return R.data(vos);
    }

    @PostMapping("/searchCount")
    @SneakyThrows
    public R<List<DocsIndexCountTreeVO>> searchCountV2(@RequestBody SearchDTO dto, HttpServletRequest request) {
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchCountV2 开始执行={}", System.currentTimeMillis());
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isEmpty(authorization)) {
            return R.fail("token为空!");
        }
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchCountV2 开始请求权限计算={}", System.currentTimeMillis());
        Object invokePermission = invokePermission(dto.getSearchType(), authorization);
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchCountV2 请求权限计算结束={}", System.currentTimeMillis());

        if (invokePermission instanceof String) {
            R<List<DocsIndexCountTreeVO>> fail = R.fail("请求权限接口失败!");
            fail.setExt((String) invokePermission);
            return fail;
        }
        List<IamCategoryIndexBean> list = (List<IamCategoryIndexBean>) invokePermission;
        if (list.size() < 1) {
            return R.fail("该用户没有任何权限!");
        }

        List<DocsIndexCountTreeVO> vos = new ArrayList<>();
        for (IamCategoryIndexBean iamCategoryIndexBean : list) {
            vos.add(extracted(dto, iamCategoryIndexBean));
        }
        for (DocsIndexCountTreeVO vo : vos) {
            vo.setTotal(vo.getTotal() + recursionTotal(vo.getInfo()));
        }
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchCountV2 执行结束={}", System.currentTimeMillis());
        return R.data(vos);
    }

    @PostMapping("/searchByCommonId")
    @SneakyThrows
    public R<DocsIndexVO> searchByCommonId(@RequestBody SearchByCommonIdDTO dto, HttpServletRequest request) {
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchByCommonId 开始执行={}", System.currentTimeMillis());


        List<String> index = new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();



        SearchResponse<Map> search = docsService.searchByCommonId(dto.getCommonId(),dto.getIndex());
        if (search == null) {
            return R.data(new DocsIndexVO());
        }
        DocsIndexVO vo = getDocsIndexVO(search.hits(), "", "");
        log.info("com.search.supersearch.controller.docs.DocsV3Controller.searchV2 执行结束={}", System.currentTimeMillis());
        return R.data(vo);
    }
    private Long recursionTotal(List<DocsIndexCountTreeVO> info) {
        long total = 0L;
        if (info == null) {
            return total;
        }
        for (DocsIndexCountTreeVO vo : info) {
            long voTotal = vo.getTotal() + recursionTotal(vo.getInfo());
            total += voTotal;
            vo.setTotal(voTotal);
        }
        return total;
    }

    private void categoryAllIndex(List<String> index, Map<String, List<String>> esOrdId_map, IamCategoryIndexBean iamCategoryIndexBean) {
        List<IamCategoryIndexBean> children = iamCategoryIndexBean.getChildren();
        if (children != null && children.size() > 0) {
            for (IamCategoryIndexBean child : children) {
                List<String> collect = child.getIndexBeanList().stream().map(IamSearchCategoryidIndexname::getIndexName).collect(Collectors.toList());
                Map<String, List<String>> map2 = child.getIndexBeanList().stream().collect(Collectors.toMap(IamSearchCategoryidIndexname::getIndexName, i -> Optional.ofNullable(i.getOrgCodes()).orElse(Lists.newArrayList())));
                esOrdId_map.putAll(map2);
                index.addAll(collect);
                categoryAllIndex(index, esOrdId_map, child);
            }
        }
    }

    private List<DocsIndexCountTreeVO> recursionInfo(SearchDTO dto, IamCategoryIndexBean iamCategoryIndexBean) {
        List<IamCategoryIndexBean> children = iamCategoryIndexBean.getChildren();
        if (children == null) {
            return null;
        }
        List<DocsIndexCountTreeVO> result = new ArrayList<>();
        for (IamCategoryIndexBean dataCategory : children) {
            result.add(extracted(dto, dataCategory));
        }
        return result;
    }

    private DocsIndexCountTreeVO extracted(SearchDTO dto, IamCategoryIndexBean iamCategoryIndexBean) {
        IamDataCategory dataCategoryBean = iamCategoryIndexBean.getDataCategoryBean();

        DocsIndexCountTreeVO vo = new DocsIndexCountTreeVO();
        vo.setType(dataCategoryBean.getCategoryId());
        vo.setName(dataCategoryBean.getCategoryName());
        vo.setInfo(recursionInfo(dto, iamCategoryIndexBean));

        List<String> index = iamCategoryIndexBean.getIndexBeanList().stream().map(IamSearchCategoryidIndexname::getIndexName).collect(Collectors.toList());
        Map<String, List<String>> map = iamCategoryIndexBean.getIndexBeanList().stream().collect(Collectors.toMap(IamSearchCategoryidIndexname::getIndexName, i -> Optional.ofNullable(i.getOrgCodes()).orElse(Lists.newArrayList())));

        if (index.size() < 1) {
            vo.setTotal(0L);
            log.info("es数量查询,索引不存在!dataCategory.getCategoryId()={},index={}", iamCategoryIndexBean.getDataCategoryBean().getCategoryId(), index);
        } else {
            CountResponse count = null;
            try {
                count = elasticsearchClient.count(c -> {
                    CountRequest.Builder countRequestBuilder = c.index(index);
                    docsService.matchCriteriaCountRequest(dto.getMatch(), index, map, dto.getKeyword(), countRequestBuilder);
                    return countRequestBuilder;
                });
                vo.setTotal(count.count());
                log.info("成功查询dataCategory.getCategoryId()={},index={}", dataCategoryBean.getCategoryId(), index);
            } catch (Exception e) {
                vo.setTotal(0L);
                log.info("失败查询dataCategory.getCategoryId()={},index={}", dataCategoryBean.getCategoryId(), index);
            }
        }
        return vo;
    }

    private DocsIndexVO getDocsIndexVO(HitsMetadata<Map> hits, String type, String name) {
        ArrayList<Object> list = Lists.newArrayList();
        List<String> searchAfter = Lists.newArrayList();



        for (Hit<Map> docsIndexHit : hits.hits()) {
            DocsIndexVO.DocsIndexInfo info = new DocsIndexVO.DocsIndexInfo();
            Map source = docsIndexHit.source();
            assert source != null;
            String es_content = (String) source.get("es_data_simple_content");
            if (StringUtils.isNotEmpty(es_content)) {
                source.put("es_data_content", es_content);

                if (StringUtils.isNotEmpty(es_content) && es_content.length() > 50) {
                    source.put("es_data_content", es_content.substring(0, 50));
                }

            }

            String path = (String) source.get("es_file_path");
            if (StringUtils.isNotBlank(path)) {
                source.put("es_file_path", path.substring(path.lastIndexOf("/") + 1));

            }
            info.setSource(source);
            info.setHighlight(docsIndexHit.highlight());
            List<String> collect = docsIndexHit.sort().stream().map(v -> v._get() + "").collect(Collectors.toList());
            info.setSearchAfter(collect);
            searchAfter = collect;
            list.add(info);
        }

        DocsIndexVO vo = new DocsIndexVO();
        vo.setTotal(hits.total().value());
        vo.setData(list);
        vo.setType(type);
        vo.setName(name);
        return vo;
    }


    private Object invokePermission(String id, String token) {
        if (StringUtils.isEmpty(id)) {
            id = "";
        }
        log.info("数据权限请求参数={}", id);
        String authorization = HttpRequest.post(url + id)
                .header("Authorization", token)
//                .body(format)
                .timeout(20000)
                .execute().body();
        log.info("数据权限请求结果={}", authorization);
        List<IamCategoryIndexBean> iamCategoryIndexBeans = null;
        try {
            iamCategoryIndexBeans = JSONUtil.toList(authorization, IamCategoryIndexBean.class);
        } catch (Exception e) {

            log.error("数据权限请求结果={}", authorization);
            return authorization;
        }

        return iamCategoryIndexBeans;
    }

}
