package com.ylx.area.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylx.common.config.AmapConfig;
import com.ylx.common.exception.ServiceException;
import com.ylx.common.utils.http.HttpUtils;
import com.ylx.massage.domain.Area;
import com.ylx.massage.domain.dto.CoordinateDTO;
import com.ylx.massage.domain.vo.*;
import com.ylx.area.mapper.AreaMapper;
import com.ylx.area.service.AreaService;
import com.ylx.area.constants.CityCodeTypeEnum;
import com.ylx.area.domain.vo.CityVo;
import com.ylx.common.utils.CityChineseUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service("areaService")
public class AreaServiceImpl extends ServiceImpl<AreaMapper, Area> implements AreaService {

    @Resource
    private AmapConfig amapConfig;

    private static final String AMAP_URL_PATTERN = "https://restapi.amap.com/v3/geocode/regeo?key=%s&location=%s,%s";

    private static final String AMAP_SUCCESS_STATUS = "1";
    private static final String ERROR_MSG_API_KEY_MISSING = "配置文件中未找到或amap.apiKey 为空";
    private static final String ERROR_MSG_LOCATION_FAILED = "获取地理位置异常";
    private static final String ERROR_MSG_CITY_NOT_FOUND = "城市数据不存在";
    private static final int MAX_QUERY_RESULTS = 1;

    private static final List<String> PROVINCE_CODE_LIST = Arrays.asList("110100", "120100", "310100", "500100");

    private final static String CITY_TYPE = "市辖区";

    /**
     * 一次性查询所有区域，内存构建树形结构
     */
    @Override
    public List<AreaTreeNode> getAreaTree() {
        // 1. 只查询需要的字段，减少数据传输与内存占用（核心优化）
        List<Area> areaList = this.list(Wrappers.lambdaQuery(Area.class)
                .select(Area::getId, Area::getCode, Area::getName, Area::getParentCode, Area::getLevel)
        );

        // 2. 转树形节点（流式处理）
        List<AreaTreeNode> allNodes = areaList.stream()
                .map(area -> {
                    AreaTreeNode node = new AreaTreeNode();
                    node.setId(area.getId());
                    node.setCode(area.getCode());
                    node.setName(area.getName());
                    node.setParentCode(area.getParentCode());
                    node.setLevel(area.getLevel());
                    node.setChildren(new ArrayList<>()); // 预先初始化，避免空指针
                    return node;
                })
                .collect(Collectors.toList());

        // 3. 构建 code -> node 映射（O(1) 快速查找，性能提升巨大）
        Map<String, AreaTreeNode> nodeMap = allNodes.stream()
                .collect(Collectors.toMap(AreaTreeNode::getCode, node -> node));

        // 4. 组装父子关系（单循环，O(n) 最优复杂度）
        for (AreaTreeNode node : allNodes) {
            String parentCode = node.getParentCode();
            AreaTreeNode parentNode = nodeMap.get(parentCode);
            if (parentNode != null) {
                parentNode.getChildren().add(node);
            }
        }

        // 5. 只返回顶级节点（parentCode = 0）
        return allNodes.stream()
                .filter(node -> "0".equals(node.getParentCode()))
                .collect(Collectors.toList());
    }

    /**
     * 查询省、市两级行政区域树。
     */
    @Override
    public List<AreaTreeNode> getProvinceCityTree() {
        List<Area> areaList = this.list(Wrappers.lambdaQuery(Area.class)
                .select(Area::getId, Area::getCode, Area::getName, Area::getParentCode, Area::getLevel)
                .in(Area::getLevel, 1, 2)
        );

        List<AreaTreeNode> allNodes = areaList.stream()
                .map(this::toAreaTreeNode)
                .collect(Collectors.toList());

        Map<String, AreaTreeNode> nodeMap = allNodes.stream()
                .collect(Collectors.toMap(AreaTreeNode::getCode, node -> node, (oldNode, newNode) -> oldNode));

        for (AreaTreeNode node : allNodes) {
            if (node.getLevel() == null || node.getLevel() != 2) {
                continue;
            }
            AreaTreeNode parentNode = nodeMap.get(node.getParentCode());
            if (parentNode != null) {
                parentNode.getChildren().add(node);
            }
        }

        return allNodes.stream()
                .filter(node -> Integer.valueOf(1).equals(node.getLevel()))
                .collect(Collectors.toList());
    }

    private AreaTreeNode toAreaTreeNode(Area area) {
        AreaTreeNode node = new AreaTreeNode();
        node.setId(area.getId());
        node.setCode(area.getCode());
        node.setName(area.getName());
        node.setParentCode(area.getParentCode());
        node.setLevel(area.getLevel());
        node.setChildren(new ArrayList<>());
        return node;
    }

    @Override
    public CityInfoVo getCityInfoByCoordinates(CoordinateDTO dto) {
        // 参数验证
        validateCoordinates(dto);

        // 获取API Key
        String amapKey = amapConfig.getApiKey();
        if (StrUtil.isEmpty(amapKey)) {
            log.error(ERROR_MSG_API_KEY_MISSING);
            throw new ServiceException(ERROR_MSG_API_KEY_MISSING);
        }

        try {
            // 调用高德API
            String url = String.format(AMAP_URL_PATTERN, amapKey, dto.getLongitude(), dto.getLatitude());
            String response = HttpUtils.sendGet(url);

            // 解析响应
            AmapRegeoVo amapResponse = parseAmapResponse(response);

            // 获取城市信息
            return getCityInfo(amapResponse);
        } catch (Exception e) {
            log.error("获取城市信息失败", e);
            throw new ServiceException(e.getMessage());
        }
    }

    private void validateCoordinates(CoordinateDTO dto) {
        if (dto == null) {
            throw new IllegalArgumentException("坐标信息不能为空");
        }

        // 1. 先判空
        if (dto.getLongitude() == null || dto.getLatitude() == null) {
            throw new IllegalArgumentException("经纬度数值不能为空");
        }

        // 2. 再比较（此时已经确定不为 null，可以安全拆箱）
        double lon = dto.getLongitude().doubleValue();
        double lat = dto.getLatitude().doubleValue();

        if (lon < -180 || lon > 180) {
            throw new IllegalArgumentException("经度必须在-180到180之间");
        }
        if (lat < -90 || lat > 90) {
            throw new IllegalArgumentException("纬度必须在-90到90之间");
        }
    }

    private AmapRegeoVo parseAmapResponse(String response) {
        if (StrUtil.isEmpty(response)) {
            throw new ServiceException(ERROR_MSG_LOCATION_FAILED);
        }

        AmapRegeoVo vo = JSONObject.parseObject(response, AmapRegeoVo.class);
        if (vo == null || !AMAP_SUCCESS_STATUS.equals(vo.getStatus())) {
            throw new ServiceException(ERROR_MSG_LOCATION_FAILED);
        }
        return vo;
    }

    /**
     * 根据经纬度获取城市信息
     *
     * @param amapResponse
     * @return CityInfoVo
     */
    private CityInfoVo getCityInfo(AmapRegeoVo amapResponse) {
        if (amapResponse == null || amapResponse.getRegeocode() == null) {
            return null;
        }

        AddressComponent addressComponent = amapResponse.getRegeocode().getAddressComponent();
        if (addressComponent == null || StrUtil.isEmpty(addressComponent.getCity())) {
            return null;
        }

        // 优化查询，只取第一条记录
        LambdaQueryWrapper<Area> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Area::getName, addressComponent.getCity()).last("LIMIT " + MAX_QUERY_RESULTS);

        Area area = this.getOne(queryWrapper);
        if (area == null) {
            throw new ServiceException(ERROR_MSG_CITY_NOT_FOUND);
        }

        return new CityInfoVo(area.getName(), area.getCode(),addressComponent.getProvince(),addressComponent.getDistrict());
    }

    /**
     * 用户首页地区查询
     * @return
     */
    @Override
    public List<CityVo> getHomeList() {
        LambdaQueryWrapper<Area> citySql = new LambdaQueryWrapper<>();
        citySql.eq(Area::getLevel, 2);
        List<Area> homeListDB = list(citySql);
        homeListDB.stream()
                .forEach(city -> {
                    if (PROVINCE_CODE_LIST.contains(city.getCode())) {
                        String cityName = CityCodeTypeEnum.getCityCodeType(city.getCode());
                        city.setName(cityName);
                    }
                });
        List<CityVo> cityList = cityConvert(homeListDB);
        cityList = cityList.stream()
                .filter(cityDTO -> ObjectUtils.isNotEmpty(cityDTO.getCityList()) && !cityDTO.getCityList().isEmpty())
                .collect(Collectors.toList());
        return cityList;
    }

    @Override
    public List<CityVo> getCityName(String name) {
        LambdaQueryWrapper<Area> citySql = new LambdaQueryWrapper<>();
        citySql.like(Area::getName, name);
        List<Area> homeList = list(citySql);
        List<CityVo> cityList = cityConvert(homeList);
        cityList = cityList.stream()
                .filter(cityVO -> ObjectUtils.isNotEmpty(cityVO.getCityList()) && !cityVO.getCityList().isEmpty())
                .collect(Collectors.toList());
        return cityList;
    }

    private List<CityVo> cityConvert(List<Area> homeListDB) {
        List<Area> homeList = homeListDB.stream()
                .filter(city -> !CITY_TYPE.equals(city.getName()))
                .collect(Collectors.toList());
        Map<String, Area> cityMap = homeList.stream().collect(Collectors.toMap(Area::getName, Area -> Area, (existing, replacement) -> existing));
//        }
        //获取所有城市名称
        List<String> cityList = cityMap.keySet().stream().collect(Collectors.toList());

        List<CityVo> cityVoList = new ArrayList<>();
        try {
            //按照首字母进行城市分组
            Map<String, List<String>> pinyingMap = CityChineseUtil.getCodeGroup(cityList);
            //将分组对象转化成前端所需格式类型
            pinyingMap.forEach((pinying, mapCityList) -> {
                CityVo cityVo = new CityVo();
                cityVo.setLetter(pinying);
                List<CityVo.City> cityNewList = new ArrayList<>();
                mapCityList.stream()
                        .forEach(city -> {
                            CityVo.City cityNew = new CityVo.City();
                            BeanUtils.copyProperties(cityMap.get(city), cityNew);
                            cityNewList.add(cityNew);
                        });
                cityVo.setCityList(cityNewList);
                cityVoList.add(cityVo);
            });
        } catch (Exception e) {
            log.error("首页城市列表拼音转换接口报错!");
            throw new RuntimeException(e);
        }
        return cityVoList;
    }
}
