package shop.alien.store.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import shop.alien.entity.result.R;
import shop.alien.entity.store.*;
import shop.alien.entity.store.vo.StoreBannerVo;
import shop.alien.entity.store.vo.StoreBusinessInfoVo;
import shop.alien.entity.store.vo.StoreBusinessStatusVo;
import shop.alien.entity.store.vo.StoreInfoVo;
import shop.alien.mapper.*;
import shop.alien.store.annotation.TrackEvent;
import shop.alien.store.service.CommonRatingService;
import shop.alien.store.service.StoreBannerService;
import shop.alien.store.service.StoreImgService;
import shop.alien.store.service.StoreInfoService;
import shop.alien.store.util.ai.AiAuthTokenUtil;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


@Slf4j
@Api(tags = {"ai搜索"})
@CrossOrigin
@RestController
@RequestMapping("/aiSearch")
@RequiredArgsConstructor
@RefreshScope
public class AiSearchController {

    private final StoreImgMapper storeImgMapper;
    private final StoreUserMapper storeUserMapper;
    private final LifeUserMapper lifeUserMapper;

    @Value("${third-party-ai-search.exact.base-url:http://124.93.18.180:7870/api/v1/search}")
    private String aiSearchExactUrl;
    
    @Value("${third-party-ai-search.fuzzy.base-url:http://124.93.18.180:7869/api/v1/search}")
    private String aiSearchFuzzyUrl;

    private final RestTemplate restTemplate;
    private final StoreImgService storeImgService;
    private final StoreBannerService storeBannerService;
    private final CommonRatingService commonRatingService;
    private final CommonRatingMapper commonRatingMapper;
    private final StoreInfoService storeInfoService;

    private final LifeBlacklistMapper lifeBlacklistMapper;
    private final AiAuthTokenUtil aiAuthTokenUtil;

    @TrackEvent(
            eventType = "SEARCH",
            eventCategory = "TRAFFIC",
            storeId = "",
            targetType = "STORE"
    )
    @RequestMapping("/search")
    public R search(@RequestBody Map<String,String> map) {


        // 初始化请求体Map
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("query", map.get("storeName"));
        requestBody.put("page_size", map.get("pageSize"));
        requestBody.put("user_lat", map.get("lat"));
        requestBody.put("user_lng", map.get("lon"));
        requestBody.put("category", map.get("category"));
        requestBody.put("page", map.get("pageNum"));
        requestBody.put("sort_by", map.get("sortBy"));
        if("distance".equals(map.get("sortBy"))){
            requestBody.put("sort_order", "asc");
        } else {
            requestBody.put("sort_order", "desc");
        }
        LifeUser lifeUser = lifeUserMapper.selectById(map.get("userId"));
        if(lifeUser!=null){
            requestBody.put("fans_id","user_"+lifeUser.getUserPhone());
        }
        HttpHeaders aiHeaders = new HttpHeaders();
        String accessToken = aiAuthTokenUtil.getAccessToken();
        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
        aiHeaders.set("Authorization", "Bearer " + accessToken);
//        aiHeaders.set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1cHN0b3JlQGFkbWluLmNvbSIsImlkIjo2LCJ0aW1lIjoxNzYyOTI1NDAzLjY1MTY5MjZ9.07lz8Ox2cGC28UCmqcKCt5R6Rfwtgs-Eiu0ttgWRxws");

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
        try {
            log.info("调用AI检索店铺列表最上面根据店铺名查询  接口入参------{}", requestBody);
            ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(aiSearchExactUrl, request, String.class);
            String body = stringResponseEntity.getBody();
            log.info("调用AI检索店铺列表最上面根据店铺名查询  接口返回------{}", body);
            JSONObject jsonObject = JSONObject.parseObject(body);
            JSONObject jsonObject1 = new JSONObject();
            // 生活服务类别：转换为StoreInfoVo，确保返回的字段名按照StoreInfoVo定义
            // 模糊搜索：从related_results和matched_results字段获取数据
            List<StoreInfoVo> relatedResult = convertToStoreInfoList(jsonObject.getJSONArray("related_results"),map.get("userId"));
            List<StoreInfoVo> matchedResult = convertToStoreInfoList(jsonObject.getJSONArray("matched_results"),map.get("userId"));

            // 并发处理图片和营业时间，提升性能
            CompletableFuture<Void> relatedImageFuture = CompletableFuture.runAsync(() -> fillStoreImages(relatedResult, 1));
            CompletableFuture<Void> matchedImageFuture = CompletableFuture.runAsync(() -> fillStoreImages(matchedResult, 1));
            CompletableFuture<Void> relatedBusinessHoursFuture = CompletableFuture.runAsync(() -> fillBusinessHours(relatedResult));
            CompletableFuture<Void> matchedBusinessHoursFuture = CompletableFuture.runAsync(() -> fillBusinessHours(matchedResult));

            // 等待所有任务完成
            CompletableFuture.allOf(relatedImageFuture, matchedImageFuture, relatedBusinessHoursFuture, matchedBusinessHoursFuture).join();

            // 合并两个列表
//            List<StoreInfoVo> relatedResults = new ArrayList<>();
//            List<StoreInfoVo> matchedResults = new ArrayList<>();
//            matchedResults.addAll(matchedResult);
//            relatedResults.addAll(relatedResult);
            jsonObject1.put("matchedRecords", matchedResult);
            jsonObject1.put("relatedRecords", relatedResult);
            // 根据matchedResult中的business_section_name去插入到storeBanners中
            List<String> sectionNames = matchedResult.stream()
                    .map(StoreInfoVo::getBusinessSectionName)
                    .filter(StringUtils::isNotBlank)
                    .distinct()
                    .collect(Collectors.toList());
            List<StoreBannerVo> storeBanners = storeBannerService.getBannerByAISearch(sectionNames);
            jsonObject1.put("storeBanners", storeBanners);


            jsonObject1.put("total", jsonObject.get("total"));
            jsonObject1.put("size", map.get("pageSize"));
            log.info("调用AI搜索店铺列表最上面根据店铺名查询 后端处理后数据接口返回------{}", jsonObject1);
            return R.data(jsonObject1);
        } catch (Exception e) {
            log.error("调用AI搜索接口 接口异常------", e);
        }
        return  R.fail("请求失败");
    }

    @RequestMapping("/fuzzySearch")
    public R fuzzySearch(@RequestBody Map<String,String> map) {
        // 初始化请求体Map
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("query", map.get("storeName"));
        requestBody.put("page_size", map.get("pageSize"));
        requestBody.put("user_lat", map.get("lat"));
        requestBody.put("user_lng", map.get("lon"));
        requestBody.put("category", map.get("category"));
        requestBody.put("page", map.get("pageNum"));
        requestBody.put("sort_by", map.get("sortBy"));
        if("distance".equals(map.get("sortBy"))){
            requestBody.put("sort_order", "asc");
        } else {
            requestBody.put("sort_order", "desc");
        }
        HttpHeaders aiHeaders = new HttpHeaders();
        String accessToken = aiAuthTokenUtil.getAccessToken();
        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
        aiHeaders.set("Authorization", "Bearer " + accessToken);

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
            log.info("调用AI首页店铺列表搜索接口 请求参数------{} AI开始时间: {}", requestBody, formatter.format(Instant.now()));
            ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(aiSearchFuzzyUrl, request, String.class);
            String body = stringResponseEntity.getBody();
            log.info("调用AI首页店铺列表搜索接口 AI接口返回------{}  AI结束时间: {}", body, formatter.format(Instant.now()));
            JSONObject jsonObject = JSONObject.parseObject(body);
            JSONObject jsonObject1 = new JSONObject();
            // 模糊搜索：从 results 字段获取数据（AI 服务返回格式）
            List<StoreInfoVo> result = convertToStoreInfoList(jsonObject.getJSONArray("results"), map.get("userId"));

            // 并发处理图片、评论数据和营业时间，提升性能
            CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> fillStoreImages(result, 1));
            CompletableFuture<Void> ratingFuture = CompletableFuture.runAsync(() -> fillRatingCountBatch(result));
            CompletableFuture<Void> businessHoursFuture = CompletableFuture.runAsync(() -> fillBusinessHours(result));
            
            CompletableFuture.allOf(imageFuture, ratingFuture, businessHoursFuture).join();

            jsonObject1.put("records", result);
            jsonObject1.put("total", jsonObject.get("total"));
            jsonObject1.put("size", map.get("pageSize"));
            log.info("调用AI首页店铺列表搜索 Java数据处理后接口返回------{}  最终时间: {}", jsonObject1, formatter.format(Instant.now()));
            return R.data(jsonObject1);
        } catch (org.springframework.web.client.HttpServerErrorException e) {
            String responseBody = e.getResponseBodyAsString();
            log.error("调用AI模糊搜索接口 上游返回5xx------ status={}, body={}", e.getStatusCode(), responseBody, e);
            // Meilisearch 报 store_application_status is not filterable 时，需在 AI 搜索服务或 Meilisearch 索引中将 store_application_status 加入 filterableAttributes，或去掉该过滤条件
            if (responseBody != null && responseBody.contains("store_application_status") && responseBody.contains("not filterable")) {
                log.error("Meilisearch 不可用 store_application_status 筛选：请在第三方 AI 搜索服务或 Meilisearch 索引配置中将 store_application_status 加入 filterableAttributes，或移除该过滤条件。参见 docs/ai-search-meilisearch-filterable.md");
            }
            return R.fail("搜索服务暂时不可用，请稍后重试");
        } catch (Exception e) {
            log.error("调用AI模糊搜索接口 接口异常------", e);
        }
        return R.fail("请求失败");
    }

    private List<StoreInfoVo> convertToStoreInfoList(JSONArray results, String  userId) {
        // 查找用户拉黑的商户
        QueryWrapper<LifeBlacklist> queryWrapper = new QueryWrapper<LifeBlacklist>();
        queryWrapper.eq("blocker_id",userId);
        queryWrapper.eq("blocker_type",2);
        queryWrapper.eq("blocked_type",1);
        List<LifeBlacklist> lifeBlacklists = lifeBlacklistMapper.selectList(queryWrapper);
        // 坑：查询出来的是拉黑的商户id，不是商铺id 😊彻底疯狂
        List<String> blockedIds = lifeBlacklists.stream().map(x -> x.getBlockedId()).collect(Collectors.toList());
        List<Integer> collect = new ArrayList<>();
        if (blockedIds.size() > 0) {
            List<StoreUser> storeUsers = storeUserMapper.selectBatchIds(blockedIds);
            // store_id 可能为 null，不可对 null 调用 toString()
            collect = storeUsers.stream()
                    .map(StoreUser::getStoreId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        }

        List<StoreInfoVo> storeInfoList = new ArrayList<>();

        if (results != null) {
            for (int i = 0; i < results.size(); i++) {
                JSONObject item = results.getJSONObject(i);
                // 记录第一条数据的字段名和内容，用于调试
                if (i == 0) {
                    log.info("AI返回的JSON字段示例: {}", item.keySet());
                }
                
                // 创建新的JSONObject，将下划线命名转换为驼峰命名
                JSONObject camelCaseItem = new JSONObject();
                for (String key : item.keySet()) {
                    String camelKey = underscoreToCamelCase(key);
                    camelCaseItem.put(camelKey, item.get(key));
                }
                
                // 将"name"字段映射到"storeName"字段
                if (camelCaseItem.containsKey("name") && !camelCaseItem.containsKey("storeName")) {
                    camelCaseItem.put("storeName", camelCaseItem.get("name"));
                }

                // 使用JSON.parseObject方法进行转换
                StoreInfoVo storeInfo = JSON.parseObject(camelCaseItem.toJSONString(), StoreInfoVo.class);
                if(collect.contains(storeInfo.getId())){
                    continue;
                }
                // 移除这里的getRatingCount调用，统一在fillRatingCountBatch中批量处理
                storeInfoList.add(storeInfo);
            }
        }
        return storeInfoList;
    }

    /**
     * 查找店铺图片并设置到StoreInfoVo列表中
     *
     * @param result StoreInfoVo列表
     * @param imgType 图片类型（1:入口图, 2:相册, 10:商家头像等）
     */
    private void fillStoreImages(List<StoreInfoVo> result, Integer imgType) {
        if (result == null || result.isEmpty()) {
            return;
        }
        
        // 从结果中提取所有的id，放到集合中
        List<Integer> storeIdList = result.stream()
                .map(StoreInfoVo::getId)
                .filter(id -> id != null)
                .distinct()
                .collect(Collectors.toList());
        
        if (storeIdList.isEmpty()) {
            return;
        }
        
        // 批量查询图片
        QueryWrapper<StoreImg> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("store_id", storeIdList)
                .eq("img_type", imgType)
                .orderByAsc("img_sort");
        List<StoreImg> storeImgList = storeImgMapper.selectList(queryWrapper);
        
        // 将图片列表按照storeId分组
        Map<Integer, List<StoreImg>> storeImgMap = storeImgList.stream()
                .collect(Collectors.groupingBy(StoreImg::getStoreId));
        
        // 将图片列表设置到对应的StoreInfoVo中
        for (StoreInfoVo storeInfo : result) {
            if (storeInfo.getId() != null) {
                List<StoreImg> imgs = storeImgMap.get(storeInfo.getId());
                if (imgs != null && !imgs.isEmpty()) {
                    // 将图片列表转换为URL列表，按照imgSort排序
                    List<String> imgUrlList = imgs.stream()
                            .sorted(Comparator.comparing(StoreImg::getImgSort, Comparator.nullsLast(Comparator.naturalOrder())))
                            .map(StoreImg::getImgUrl)
                            .filter(url -> url != null)
                            .collect(Collectors.toList());
                    storeInfo.setStoreAlbumUrlList(imgUrlList);
                    // 如果第一个图片存在，也可以设置到imgUrl字段作为头像
                    if (!imgUrlList.isEmpty()) {
                        storeInfo.setImgUrl(imgUrlList.get(0));
                    }
                }
            }
        }
    }

    /**
     * 填充评论总数到StoreInfoVo列表中（批量查询优化版本）
     * 使用批量查询替代N+1查询，大幅提升性能
     *
     * @param result StoreInfoVo列表
     */
    private void fillRatingCountBatch(List<StoreInfoVo> result) {
        if (result == null || result.isEmpty()) {
            return;
        }

        // 提取所有storeId
        List<Integer> storeIdList = result.stream()
                .map(StoreInfoVo::getId)
                .filter(id -> id != null)
                .distinct()
                .collect(Collectors.toList());

        if (storeIdList.isEmpty()) {
            return;
        }

        try {
            // 批量查询所有store的评价记录（只查询审核通过且展示的）
            LambdaQueryWrapper<CommonRating> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(CommonRating::getBusinessId, storeIdList)
                    .eq(CommonRating::getBusinessType, 1)
                    .eq(CommonRating::getAuditStatus, 1)
                    .eq(CommonRating::getIsShow, 1);
            List<CommonRating> allRatings = commonRatingMapper.selectList(wrapper);

            // 按businessId分组统计评论总数
            Map<Integer, Long> ratingCountMap = allRatings.stream()
                    .collect(Collectors.groupingBy(
                            CommonRating::getBusinessId,
                            Collectors.counting()
                    ));

            // 设置评论总数到对应的StoreInfoVo
            for (StoreInfoVo storeInfo : result) {
                if (storeInfo.getId() != null) {
                    Long count = ratingCountMap.get(storeInfo.getId());
                    storeInfo.setTotalNum(count != null ? String.valueOf(count) : "0");
                }
            }
        } catch (Exception e) {
            log.error("批量获取评论总数失败", e);
            // 如果批量查询失败，回退到单个查询（兜底策略）
            for (StoreInfoVo storeInfo : result) {
                if (storeInfo.getId() != null) {
                    try {
                        Object ratingCountObj = commonRatingService.getRatingCount(storeInfo.getId(), 1);
                        if (ratingCountObj instanceof Map) {
                            Map<String, Object> ratingCountMap = (Map<String, Object>) ratingCountObj;
                            Object totalCount = ratingCountMap.get("totalCount");
                            if (totalCount != null) {
                                storeInfo.setTotalNum(String.valueOf(totalCount));
                            } else {
                                storeInfo.setTotalNum("0");
                            }
                        } else {
                            storeInfo.setTotalNum("0");
                        }
                    } catch (Exception ex) {
                        log.warn("获取评论总数失败，storeId: {}", storeInfo.getId(), ex);
                        storeInfo.setTotalNum("0");
                    }
                }
            }
        }
    }

    /**
     * 批量填充营业时间到StoreInfoVo列表中
     * 通过调用storeInfoService.getStoreInfoBusinessHours方法获取营业时间（包含节假日信息）
     *
     * @param result StoreInfoVo列表
     */
    private void fillBusinessHours(List<StoreInfoVo> result) {
        if (result == null || result.isEmpty()) {
            return;
        }

        // 遍历result集合，为每个门店调用getStoreInfoBusinessHours方法
        for (StoreInfoVo storeInfo : result) {
            if (storeInfo.getId() != null) {
                try {
                    // 调用getStoreInfoBusinessHours方法获取营业时间（包含节假日信息）
                    List<StoreBusinessInfoVo> storeBusinessInfoVos = storeInfoService.getStoreInfoBusinessHours(storeInfo.getId());
                    if (storeBusinessInfoVos != null && !storeBusinessInfoVos.isEmpty()) {
                        // 设置包含节假日信息的营业时间列表
                        storeInfo.setStoreBusinessInfoVos(storeBusinessInfoVos);
                        // 转换为 List<StoreBusinessInfo> 用于兼容原有字段（StoreBusinessInfoVo 继承自 StoreBusinessInfo）
                        List<StoreBusinessInfo> storeBusinessInfos = new ArrayList<>(storeBusinessInfoVos);
                        storeInfo.setStoreBusinessInfos(storeBusinessInfos);
                        // 设置第一个营业时间信息到 storeBusinessInfo 字段
                        if (!storeBusinessInfos.isEmpty()) {
                            storeInfo.setStoreBusinessInfo(storeBusinessInfos.get(0));
                        }
                        // 同时设置到openTime字段（格式化为字符串列表）
                        List<String> openTimeList = storeBusinessInfoVos.stream()
                                .map(info -> {
                                    if (info.getBusinessDate() != null && info.getStartTime() != null && info.getEndTime() != null) {
                                        return info.getBusinessDate() + " " + info.getStartTime() + "-" + info.getEndTime();
                                    }
                                    return null;
                                })
                                .filter(time -> time != null)
                                .collect(Collectors.toList());
                        storeInfo.setOpenTime(openTimeList);
                    }
                    // 获取营业状态（通过getStoreBusinessStatus方法）
                    StoreBusinessStatusVo businessStatus = storeInfoService.getStoreBusinessStatus(String.valueOf(storeInfo.getId()));
                    if (businessStatus != null && businessStatus.getYyFlag() != null) {
                        storeInfo.setYyFlag(businessStatus.getYyFlag());
                    }
                } catch (Exception e) {
                    log.error("获取门店营业时间失败，storeId: {}", storeInfo.getId(), e);
                }
            }
        }
    }

    /**
     * 将下划线命名转换为驼峰命名
     * 例如: store_tel -> storeTel, created_time -> createdTime
     */
    private String underscoreToCamelCase(String underscore) {
        if (underscore == null || !underscore.contains("_")) {
            return underscore;
        }
        StringBuilder camelCase = new StringBuilder();
        boolean nextUpperCase = false;
        for (char c : underscore.toCharArray()) {
            if (c == '_') {
                nextUpperCase = true;
            } else {
                if (nextUpperCase) {
                    camelCase.append(Character.toUpperCase(c));
                    nextUpperCase = false;
                } else {
                    camelCase.append(c);
                }
            }
        }
        return camelCase.toString();
    }
























}
