package com.microservices.cms.search.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.microservices.cms.doc.domain.CmsDoc;
import com.microservices.cms.doc.domain.CmsDocWithScore;
import com.microservices.cms.doc.mapper.CmsDocMapper;
import com.microservices.cms.doc.service.ICmsDocService;
import com.microservices.cms.search.service.IGlobalSearchService;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.redis.service.RedisService;
import com.microservices.cms.search.dto.DocSearchRequest;
import com.microservices.cms.search.dto.DocSearchResponse;
import com.microservices.cms.search.dto.DocSearchResult;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.model.LoginUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * 全局搜索服务实现
 *
 * @author microservices
 */
@Service
public class GlobalSearchServiceImpl implements IGlobalSearchService {

    private static final Logger logger = LoggerFactory.getLogger(GlobalSearchServiceImpl.class);

    @Autowired
    private CmsDocMapper cmsDocMapper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private ICmsDocService cmsDocService;

    private static final String SEARCH_CACHE_PREFIX = "search:cache:";
    private static final Long CACHE_EXPIRE_SECONDS = 300L; // 5分钟

    @Override
    public DocSearchResponse docSearch(DocSearchRequest request) {
        long startTime = System.currentTimeMillis();

        // 参数校验
        if (StringUtils.isEmpty(request.getKeyword())) {
            return buildEmptyResponse(request);
        }

        // 1. 尝试从缓存获取（仅第一页）
        if (request.getPageNum() == 1) {
            String cacheKey = buildCacheKey(request);

            DocSearchResponse cached = redisService.getCacheObject(SEARCH_CACHE_PREFIX + cacheKey);
            if (cached != null) {
                cached.setSearchTime(System.currentTimeMillis() - startTime);
                return cached;
            }
        }

        // 2. 并行搜索各类型
        List<DocSearchResult> allResults = new ArrayList<>();
        Map<String, Long> typeCounts = new HashMap<>();

        // 获取当前用户信息
        LoginUser loginUser = SecurityUtils.getLoginUser();

        // 搜索CMS文章
        List<DocSearchResult> docResults = searchDocs(request);
        allResults.addAll(docResults);
        typeCounts.put("doc", (long) docResults.size());

        // 3. 按相关度评分排序
        allResults.sort((a, b) -> {
            if (b.getScore() != null && a.getScore() != null) {
                return b.getScore().compareTo(a.getScore());
            }
            return 0;
        });

        // 4. 分页
        int total = allResults.size();
        int start = (request.getPageNum() - 1) * request.getPageSize();
        int end = Math.min(start + request.getPageSize(), total);
        List<DocSearchResult> pagedResults = start < total ?
                allResults.subList(start, end) : new ArrayList<>();

        // 5. 构建响应
        DocSearchResponse response = new DocSearchResponse();
        response.setTotal((long) total);
        response.setResults(pagedResults);
        response.setTypeCounts(typeCounts);
        response.setKeyword(request.getKeyword());
        response.setPageNum(request.getPageNum());
        response.setPageSize(request.getPageSize());
        response.setSearchTime(System.currentTimeMillis() - startTime);

        // 6. 缓存结果（仅第一页）
        if (request.getPageNum() == 1) {
            String cacheKey = buildCacheKey(request);
            redisService.setCacheObject(SEARCH_CACHE_PREFIX + cacheKey, response, CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
        }

        // 7. 记录搜索日志
        recordSearchLog(request, response, loginUser);

        return response;
    }

    /**
     * 记录搜索日志
     */
    private void recordSearchLog(DocSearchRequest request, DocSearchResponse response, LoginUser loginUser) {
        Long userId = loginUser != null ? loginUser.getUserId() : null;
        String searchTypes = "文章";
        Long deptId = cmsDocService.getDeptIdByZoneId(request.getZoneId());
        logger.info("全局搜索 - 关键词: {}, 用户ID: {}, 耗时: {}ms, 结果数: {}, 类型: {},  专区Id: {},  组织ID: {}",
                request.getKeyword(),
                userId,
                response.getSearchTime(),
                response.getTotal(),
                searchTypes,
                request.getZoneId(),
                deptId
        );
    }

    /**
     * 搜索CMS文章
     */
    private List<DocSearchResult> searchDocs(DocSearchRequest request) {
        List<DocSearchResult> results = new ArrayList<>();
        try {
            // 使用部门ID参数
            Long deptId = cmsDocService.getDeptIdByZoneId(request.getZoneId());

            // 使用新的带评分的查询方法
            List<CmsDocWithScore> docs = cmsDocMapper.globalSearchWithScore(
                    request.getKeyword(),
                    deptId,
                    (request.getPageNum() - 1) * request.getPageSize(),
                    request.getPageSize()
            );

            for (CmsDocWithScore doc : docs) {
                DocSearchResult result = new DocSearchResult();
                result.setId(doc.getId());
                result.setTitle(highlightKeyword(doc.getName(), request.getKeyword()));
                result.setContent(highlightKeyword(doc.getSummary(), request.getKeyword()));
                result.setDeptId(doc.getDeptId());
                result.setCreateTime(doc.getCreateTime());

                // 使用数据库返回的实际搜索评分
                Double score = doc.getSearchScore();
                if (score == null || score == 0.0) {
                    // 对于 LIKE 匹配（无全文评分），赋予最小评分
                    score = 0.1;
                }
                result.setScore(score);

                JSONObject extendData = new JSONObject();
                extendData.put("visits", doc.getVisits());
                extendData.put("publishTime", doc.getPublishTime());
                result.setExtendData(extendData);
                results.add(result);
            }
        } catch (Exception e) {
            // 记录错误但继续处理其他类型
            System.err.println("搜索CMS文章失败: " + e.getMessage());
        }
        return results;
    }

    /**
     * 构建缓存键
     */
    private String buildCacheKey(DocSearchRequest request) {
        Long deptId = cmsDocService.getDeptIdByZoneId(request.getZoneId());
        StringBuilder key = new StringBuilder(request.getKeyword());
        key.append(":").append(String.join(",", "Doc"));
        if (deptId != null) {
            key.append(":dept=").append(deptId);
        }
        return key.toString();
    }

    /**
     * 构建空响应
     */
    private DocSearchResponse buildEmptyResponse(DocSearchRequest request) {
        DocSearchResponse response = new DocSearchResponse();
        response.setTotal(0L);
        response.setResults(new ArrayList<>());
        response.setTypeCounts(new HashMap<>());
        response.setKeyword(request.getKeyword());
        response.setPageNum(request.getPageNum());
        response.setPageSize(request.getPageSize());
        response.setSearchTime(0L);
        return response;
    }

    /**
     * 将文本中的关键词用 <em> 标签包裹（不区分大小写）
     */
    private String highlightKeyword(String text, String keyword) {
        if (StringUtils.isEmpty(text) || StringUtils.isEmpty(keyword)) {
            return text;
        }
        return text.replaceAll("(?i)(" + Pattern.quote(keyword) + ")", "<em>$1</em>");
    }
}
