package com.sikey.wa04.business.application.service.location;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sikey.wa04.business.application.converter.MessageConverter;
import com.sikey.wa04.business.application.service.websocket.NatsPublisherService;
import com.sikey.wa04.business.domain.adapter.*;
import com.sikey.wa04.business.domain.entity.*;
import com.sikey.wa04.business.domain.entity.message.Message;
import com.sikey.wa04.business.domain.usecase.ChildIdFactory;
import com.sikey.wa04.business.domain.usecase.CoordinateConverter;
import com.sikey.wa04.business.infrastructure.adapter.RabbitMQMessaging;
import com.sikey.wa04.business.infrastructure.adapter.RedisMessageIdIncrementingAdapter;
import com.sikey.wa04.business.infrastructure.adapter.amap.AmapRequestService;
import com.sikey.wa04.business.infrastructure.adapter.amap.AmapResponse;
import com.sikey.wa04.business.infrastructure.adapter.baidumap.BaiduMapRequestService;
import com.sikey.wa04.business.infrastructure.adapter.weather.BaiduWeatherService;
import com.sikey.wa04.business.infrastructure.configuration.NatsConfig;
import com.sikey.wa04.business.infrastructure.dto.model.DeviceModel;
import com.sikey.wa04.business.infrastructure.dto.request.ask.LocationAskRequest;
import com.sikey.wa04.business.infrastructure.dto.request.iot.IotRequest;
import com.sikey.wa04.business.infrastructure.dto.request.trajectory.LocationTrajectoryRequest;
import com.sikey.wa04.business.infrastructure.dto.request.user.NotifyRequest;
import com.sikey.wa04.business.infrastructure.dto.request.user.OnlineRequest;
import com.sikey.wa04.business.infrastructure.dto.response.baidu.BaiduLocationResponse;
import com.sikey.wa04.business.infrastructure.dto.response.iot.IotResponse;
import com.sikey.wa04.business.infrastructure.dto.response.trajectory.LocationTrajectoryResponse;
import com.sikey.wa04.business.infrastructure.repository.DeviceRepository;
import com.sikey.wa04.common.constants.HeaderConstant;
import com.sikey.wa04.common.dto.header.GenericManufacturerHeader;
import com.sikey.wa04.common.dto.message.AskLocationPointNoticeMessage;
import com.sikey.wa04.common.dto.message.LocationEnterDangerFenceNoticeAppMessage;
import com.sikey.wa04.common.dto.message.LocationExitSafetyFenceNoticeAppMessage;
import com.sikey.wa04.common.enums.*;
import com.sikey.wa04.common.interceptor.CurrentRequestUser;
import com.sikey.wa04.common.utils.IpUtil;
import com.sikey.wa04.common.utils.LocationUtil;
import com.sikey.wa04.common.utils.UserUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.gavaghan.geodesy.GlobalCoordinates;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.Instant;
import java.util.*;

@Slf4j
@Service
public class LocationService {

    @Resource
    private RedisMessageIdIncrementingAdapter redisMessageIdIncrementingAdapter;

    @Resource
    private RabbitMQMessaging rabbitMQMessaging;

    @Resource
    private MessageAdapter messageAdapter;

    @Resource
    private AmapRequestService amapRequestService;

    @Resource
    private BaiduMapRequestService aMapBaiduRequestService;

    @Resource
    private MapTrajectoryAdapter mapTrajectoryAdapter;

    @Resource
    private MapTrajectoryLogAdapter mapTrajectoryLogAdapter;

    @Resource
    private DeviceQueryAdapter deviceQueryAdapter;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private DeviceRepository deviceRepository;

    @Resource
    private LocationAdapter locationAdapter;

    @Resource
    private ChildFenceAdapter childFenceAdapter;

    @Resource
    private UserAdapter userAdapter;

    @Resource
    private ChildAdapter childAdapter;

    @Resource
    private MessageCenterAdapter messageCenterAdapter;

    @Resource
    private ChildQueryAdapter childQueryAdapter;

    @Qualifier("redisTemplate")
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private NotifyAdapter notifyAdapter;

    @Resource
    private NatsPublisherService natsPublisherService;

    @Resource
    private NatsConfig natsConfig;

    @Qualifier("myTaskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor myTaskExecutor;

    @Resource
    private DeviceConfigAdapter deviceConfigAdapter;

    @Resource
    private BaiduWeatherService baiduWeatherService;

    // trainSpeed the train has moved at a speed of 83 meters per second
    private float trainSpeed = 0.0833333333F; //  300 / (1 * 60 * 60) = 0.0833333333 km/s

    /**
     * 询问设备位置
     *
     * @param request 位置请求
     */
    public void ask(LocationAskRequest request) {
        log.info("Ask location {}", request);

        AskLocationPointNoticeMessage noticeMessage = new AskLocationPointNoticeMessage();
        noticeMessage.setMsgId(redisMessageIdIncrementingAdapter.incr()).setMsgType(MessageType.ASK_LOCATION_POINT_NOTICE_MESSAGE).setSendId(CurrentRequestUser.get().getId()).setSendTime(Instant.now().toEpochMilli()).setRecvId(request.getCid());
        noticeMessage.setContent(new AskLocationPointNoticeMessage.AskLocationPointNoticeContent().setTraceId(request.getTraceId()));

        messageAdapter.save(MessageConverter.formMessaging(noticeMessage));
        rabbitMQMessaging.publish(request.getHeader(), noticeMessage);
    }

    /**
     * 轨迹
     *
     * @param request 请求
     * @return {@link LocationTrajectoryResponse}
     */
    public LocationTrajectoryResponse trajectory(LocationTrajectoryRequest request) {
        log.info("[历史轨迹]参数：{}", JSONUtil.toJsonStr(request));
        Instant start = Instant.ofEpochSecond(Long.parseLong(request.beginTime()));
        Instant end = Instant.ofEpochSecond(Long.parseLong(request.endTime()));
        List<Location> locations = locationAdapter.findByChildIdAndCreatedAtBetween(ChildIdFactory.create(request.cid()), start, end);
        //数据转换为对外结构体
        List<LocationTrajectoryResponse.TrajectoryResult> results = new ArrayList<>();
        for (Location location : locations) {
            LocationTrajectoryResponse.TrajectoryResult result = new LocationTrajectoryResponse.TrajectoryResult();
            result.setLatitude(location.getAddress().getLatitude()).setLongitude(location.getAddress().getLongitude()).setRadius(location.getAddress().getRadius()).setPointType(Integer.parseInt(location.getType().getTargetValue())).setPositionTime(location.getPositionTime().toEpochMilli());
            results.add(result);
        }
        //log.info("[轨迹]返回数据：{}", JSONUtil.toJsonPrettyStr(results));
        return new LocationTrajectoryResponse().setResults(results);
    }

    /**
     * <h5>IOT</h5>
     * 定位类型判断
     * {@link LocationType#BASE_STATION BASE_STATION} and {@link LocationType#WIFI WIFI} :
     * <br/>
     * 请查看 {@link LocationService#baidumaps(IotRequest, HttpServletRequest)}, 实际是使用百度的接口请求了基站定位WIFI定位
     * <br/>
     * {@link LocationType#GPS GPS} :
     * <br/>
     * 请查看 {@link  LocationService#gps(IotRequest)}
     *
     * @param request 请求
     * @param req     req
     * @param req     genericManufacturerHeader
     * @return ResponseEntity<Object> 返回
     * @see LocationType
     */
    public IotResponse iot(IotRequest request, HttpServletRequest req, GenericManufacturerHeader genericManufacturerHeader) throws Exception {
        log.info("[定位]请求参数：{}，header中的参数：{}", request, JSONUtil.toJsonPrettyStr(genericManufacturerHeader));

        String childId = ChildIdFactory.create(CurrentRequestUser.get().getId()).getId();

        // 通过 Ticket 查询设备
        Optional<DeviceModel> deviceModelOptional = deviceRepository.findByTicket(request.ticket());
        if (deviceModelOptional.isEmpty()) {
            log.warn("[定位]不存在设备，设备ticket：{}", request.ticket());
            throw new RuntimeException("不存在设备");
        }

        DeviceModel deviceModel = deviceModelOptional.get();
        log.info("[定位]设备数据：{}", JSONUtil.toJsonPrettyStr(deviceModel));

        String imei = deviceModel.getImei();
        String softWareVersion = genericManufacturerHeader.getSoftWareVersion();
        String model = genericManufacturerHeader.getDeviceModel();

        myTaskExecutor.execute(() -> {
            // 更新设备版本号
            deviceRepository.updateVersionAndModelById(deviceModel.getId(), softWareVersion, model);

            // 设备配置
            deviceConfigAdapter.deviceConfigMessageCenter(childId, genericManufacturerHeader);
        });

        Instant positionTime = Instant.now();
        LocationType locationType = LocationType.valueOfValue(request.accesstype());

        req.setAttribute("imei", imei);
        Location location = switch (locationType) {
            case WIFI, BASE_STATION -> baidumaps(request, req);
            case GPS -> gps(request);
        };

        if (Objects.isNull(location)) {
            log.warn("[定位]不存在定位数据");
            return new IotResponse();
        }

        location.setPositionTime(positionTime);

        GlobalCoordinates coordinates = location.getCoordinates();

        // 非大陆位置
        double lat = coordinates.getLatitude();
        double lon = coordinates.getLongitude();
        boolean isPositionInHongKong = isPositionInHongKong(lat, lon);
        if (isPositionInHongKong) {
            CoordinateConverter coordinateHongKongConverter = coordinateHongKongConverter();
            location.setCoordinates(coordinateHongKongConverter.GCJ02toWGS84(lat, lon));
            log.info("[定位]香港位置，原先经度：{}，纬度：{}，转换后经度：{}，纬度：{}", lat, lon, location.getAddress().getLatitude(), location.getAddress().getLongitude());
        }

        // 检查用户的位置是否超出围栏
        crossingBoundaries(childId, location.getAddress().getLatitude(), location.getAddress().getLongitude(), genericManufacturerHeader);

        location.setDeviceId(new DeviceId(deviceModel.getId()));
        log.info("[定位]半径数据为：{}", location.getAddress().getRadius());

        IotResponse iotResponse = new IotResponse();
        if (location.getAddress().getRadius() != -1) {
            locationAdapter.save(location, false);
            //当存在城市id时，查询天气
            if (location.getAddress().getAdCode() !=null) {
                Weather weather = baiduWeatherService.getWeather(location.getAddress().getAdCode());
                iotResponse.setWeather(new IotResponse.Weather()
                        .setCode(weather.getTextCode())
                        .setMyTextCode(weather.getMyTextCode())
                        .setHigh(weather.getHigh())
                        .setLow(weather.getLow())
                        .setNow(weather.getNow())
                );
            }
        }

        iotResponse.setAddress(location.getAddress().getDesc()).setCity(location.getAddress().getCity()).setCityId(location.getAddress().getCityCode());
        log.info("[定位]保存定位数据：{}，返回数据：{}", JSONUtil.toJsonPrettyStr(location), JSONUtil.toJsonPrettyStr(iotResponse));
        return iotResponse;
    }

    /**
     * 正在围栏规则校验
     *
     * @param cid       儿童id
     * @param latitude  纬度
     * @param longitude 经度
     * @return
     */
    private void crossingBoundaries(String cid, String latitude, String longitude, GenericManufacturerHeader header) {
        log.info("[正在围栏规则校验]儿童id：{}，纬度：{}，经度：{}", cid, latitude, longitude);
        List<ChildFence> childFenceList = childFenceAdapter.findNotDisableByChildrenId(cid, true);
        if (CollectionUtil.isEmpty(childFenceList)) {
            log.info("[IOT定位]围栏,不存在小孩围栏");
            return;
        }

        // 查询孩子信息
        Child child = childAdapter.queryChild(new ChildId(cid));
        if (Objects.isNull(child)) {
            log.info("[IOT定位]围栏,不存在该儿童");
            return;
        }

        // 总围栏开关是否已关闭
        if (Objects.nonNull(child.getIsEnableFence()) && !child.getIsEnableFence()) {
            log.info("[IOT定位]围栏,总围栏开关已关闭");
            return;
        }
        String sendId = CurrentRequestUser.get().getId();
        childFenceList.forEach(fence -> {
            if (Objects.nonNull(fence.getLatitude()) && Objects.nonNull(fence.getLongitude())) {
                // Unit is kilometers meters
                double lat = Double.parseDouble(fence.getLatitude());
                double lon = Double.parseDouble(fence.getLongitude());
                double distanceDiff = LocationUtil.haversine(lat, lon, Double.parseDouble(latitude), Double.parseDouble(longitude));
                boolean warning = false;
                Integer fenceType = fence.getFenceType();
                log.info("[IOT定位]围栏：围栏纬度lat：{}，围栏经度lon：{} 围栏半径：{},当前点纬度lat：{}，围栏经度lon：{}，当前点和围栏中心点距离:{}", lat, lon, fence.getRange(),
                        latitude, longitude, distanceDiff);
                if (Objects.nonNull(fenceType)) {
                    if (fenceType.equals(FenceType.SAFETY.getValue())) {
                        log.info("[IOT定位]安全围栏：当前点和围栏中心点距离是否大于围栏的半径:{}", distanceDiff > fence.getRange());
                        // 围栏类型:安全围栏, 在安全围栏外需要警报
                        // 如果上一个点位是进入安全围栏的点位, 但是这次又出去了, 这时说明用户是另外一次出安全围栏
                        if (lastPositionEnterFence(cid, lat, lon, fence) && distanceDiff > fence.getRange()) {
                            log.info("[IOT定位]安全围栏：离开安全围栏警报通知");
                            warning = true;
                        }
                    } else if (fenceType.equals(FenceType.DANGER.getValue())) {
                        log.info("[IOT定位]危险围栏：当前点和围栏中心点距离是否小于围栏的半径:{}", distanceDiff < fence.getRange());
                        // 围栏类型:危险围栏, 进入围栏需要警报
                        // 如果上一个点位是出危险围栏, 但是这次又进入了, 说明用户是另外一次进入危险围栏
                        if (lastPositionLeaveFence(cid, lat, lon, fence) && distanceDiff < fence.getRange()) {
                            log.info("[IOT定位]危险围栏：进入危险围栏警报通知");
                            warning = true;
                        }
                    }
                }
                // 给孩子的消息中心信箱加入一条消息, 并且通知监护人需要注意
                if (warning) {
                    myTaskExecutor.execute(() -> {
                        try{
                            // 加入消息到消息中心
                            MessageCenter<MessageParams> messageCenter = new MessageCenter<>();
                            messageCenter.setCid(cid);
                            messageCenter.setMessageType(MessageCenterType.MESSAGE_CENTER_TYPE_ELECTRONIC_FENCE.getValue());

                            MessageParams messageParams = new MessageParams();
                            messageParams.setFid(fence.getId());
                            messageParams.setName(child.getName());
                            messageParams.setTime(Instant.now().getEpochSecond());
                            messageParams.setFenceName(fence.getFenceName());
                            messageParams.setFenceType(fence.getFenceType());
                            messageCenter.setMessageParams(messageParams);
                            messageCenterAdapter.save(messageCenter, MessageCenterType.MESSAGE_CENTER_TYPE_ELECTRONIC_FENCE);

                            // 通知监护人
                            List<ChildRef> childRefs = childQueryAdapter.queryChildRefByChildId(cid);
                            childRefs.stream().filter(cd -> Objects.nonNull(cd.getIdentity()) && UserUtil.isIdentityAdmin(IdentityEnum.valueOf(cd.getIdentity()))).forEach(cd -> {
                                log.info("[IOT]围栏 start Send message, 身份: {}", cd.getIdentity());
                                String userId = cd.getUserId();
                                Message message = new Message();
                                if (fenceType.equals(FenceType.SAFETY.getValue())) {//安全围栏
                                    LocationExitSafetyFenceNoticeAppMessage noticeMessage = new LocationExitSafetyFenceNoticeAppMessage();
                                    noticeMessage.setMsgType(MessageType.LOCATION_EXIT_SAFETY_FENCE_NOTICE_MESSAGE)
                                            .setMsgId(redisMessageIdIncrementingAdapter.incr())
                                            .setSendTime(Instant.now().toEpochMilli())
                                            .setSendId(sendId)
                                            .setRecvId(userId);

                                    LocationExitSafetyFenceNoticeAppMessage.LocationExitSafetyFenceNoticeAppContent content = new LocationExitSafetyFenceNoticeAppMessage.LocationExitSafetyFenceNoticeAppContent();
                                    content.setFid(fence.getId());
                                    content.setFenceName(fence.getFenceName());
                                    content.setFenceType(fence.getFenceType());
                                    content.setName(child.getName());
                                    content.setTime(Instant.now().toEpochMilli());
                                    noticeMessage.setContent(content);

                                    message = MessageConverter.formMessaging(noticeMessage);
                                    log.info("[IOT]安全围栏 Send message to receiver: {}", noticeMessage.getRecvId());
                                } else if (fenceType.equals(FenceType.DANGER.getValue())) {//危险围栏
                                    LocationEnterDangerFenceNoticeAppMessage noticeMessage = new LocationEnterDangerFenceNoticeAppMessage();
                                    noticeMessage.setMsgType(MessageType.LOCATION_ENTER_DANGER_FENCE_NOTICE_MESSAGE)
                                            .setMsgId(redisMessageIdIncrementingAdapter.incr())
                                            .setSendTime(Instant.now().toEpochMilli())
                                            .setSendId(sendId)
                                            .setRecvId(userId);

                                    //设置内容
                                    LocationEnterDangerFenceNoticeAppMessage.LocationEnterDangerFenceNoticeAppContent content = new LocationEnterDangerFenceNoticeAppMessage.LocationEnterDangerFenceNoticeAppContent();
                                    content.setFid(fence.getId());
                                    content.setFenceName(fence.getFenceName());
                                    content.setFenceType(fence.getFenceType());
                                    content.setName(child.getName());
                                    content.setTime(Instant.now().toEpochMilli());
                                    noticeMessage.setContent(content);

                                    message = MessageConverter.formMessaging(noticeMessage);
                                    log.info("[IOT]危险围栏 Send message to receiver: {}", noticeMessage.getRecvId());
                                }
                                //保存消息
                                messageAdapter.save(message);
                                //发送围栏告警mq消息
                                Object messagingObject = MessageConverter.toMessaging(message);
                                rabbitMQMessaging.publish(header, messagingObject);
                            });
                        } catch (Throwable e) { // 捕获所有异常和错误（包括 Error）
                            log.error("异步任务执行失败", e); // 打印错误日志
                        }

                    });

                }
            }

        });
    }

    /**
     * 请求id
     *
     * @return
     */
    private String requestId() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request.getHeader(HeaderConstant.REQUEST_ID);
    }

    /**
     * 存离线消息, 会从离线消息里获取得到最新的 ack id, 这个通常叫做 seq
     *
     * @param seq
     * @param message
     * @return
     */
    private Long offlineNotification(Long seq, NotifyRequest message) {
        String messageKey = String.format("clients.offline_notification.%s", message.Receiver());

        try {
            // 值序列化为 JSON
            Boolean result = redisTemplate.opsForHash().putIfAbsent(messageKey, String.valueOf(seq), JSONUtil.toJsonStr(message));
            return result ? seq : -1L;
        } catch (Exception e) {
            throw new RuntimeException("存离线消息异常");
        }
    }

    /**
     * 获取在线用户
     *
     * @param userId 用户id
     * @return
     */
    private OnlineRequest getOnline(String userId) {
        String key = String.format("clients.online.*.%s", userId);

        ScanOptions options = ScanOptions.scanOptions().match(key).count(100).build();

        try (Cursor<String> cursor = redisTemplate.scan(options)) {
            if (cursor.hasNext()) {
                key = cursor.next();
                Object jsonStr = redisTemplate.opsForValue().get(key);
                if (jsonStr == null) {
                    return null;
                }

                JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
                return jsonObject.toBean(OnlineRequest.class);
            }
        } catch (Exception e) {
            throw new RuntimeException("短信每日发送次数超限");
        }
        return null;
    }

    /**
     * 上一个点位是否在围栏里
     *
     * @param cId
     * @param lat
     * @param lon
     * @param childFence
     * @return
     */
    private boolean lastPositionEnterFence(String cId, double lat, double lon, ChildFence childFence) {
        ChildId childId = new ChildId(cId);
        Location location = locationAdapter.findLastLocation(childId);
        if (Objects.nonNull(location)) {
            double distanceDiff = LocationUtil.haversine(lat, lon, location.getCoordinates().getLatitude(), location.getCoordinates().getLongitude());
            // 只要有进入的点位就直接返回
            return distanceDiff < childFence.getRange();
        }
        return false;
    }

    /**
     * 上一个点位是否出了围栏
     *
     * @param cId
     * @param lat
     * @param lon
     * @param childFence
     * @return
     */
    private boolean lastPositionLeaveFence(String cId, double lat, double lon, ChildFence childFence) {
        ChildId childId = new ChildId(cId);
        Location location = locationAdapter.findLastLocation(childId);
        if (Objects.nonNull(location)) {
            double distanceDiff = LocationUtil.haversine(lat, lon, location.getCoordinates().getLatitude(), location.getCoordinates().getLongitude());
            // 只要有进入的点位就直接返回
            return distanceDiff > childFence.getRange();
        }

        return false;
    }

    /**
     * 位置是否在香港
     *
     * @param latitude  精度
     * @param longitude 纬度
     * @return
     */
    private boolean isPositionInHongKong(double latitude, double longitude) {
        CoordinateConverter coordinateHongKongConverter = coordinateHongKongConverter();
        List<List<GlobalCoordinates>> getPoints = coordinateHongKongConverter.getPoints(CoordinateConverter.CoordinatePoints.CHINA_HONG_KONG);

        CoordinateConverter.RayCastingAlgorithm rayCastingAlgorithm = new CoordinateConverter.RayCastingAlgorithm();
        GlobalCoordinates globalCoordinates = new GlobalCoordinates(latitude, longitude);
        return rayCastingAlgorithm.isPointInPolygonAreas(globalCoordinates, getPoints);
    }

    /**
     * 香港实例
     *
     * @return
     */
    private CoordinateConverter coordinateHongKongConverter() {
        CoordinateConverter.CoordinatePoints chinaHongKong = CoordinateConverter.CoordinatePoints.CHINA_HONG_KONG;
        return CoordinateConverter.getInstance(chinaHongKong);
    }

    /*private Location amap(IotRequest request) throws Exception {
        ChildId childId = ChildIdFactory.create(CurrentRequestUser.get().getId());
        return amapRequestService.position(childId, request);
    }*/

    /**
     * 百度
     *
     * @param request
     * @return
     */
    private Location baidumaps(IotRequest request, HttpServletRequest req) throws Exception {
        ChildId childId = ChildIdFactory.create(CurrentRequestUser.get().getId());
        return position(childId, request, req);
    }

    /**
     * Get the location of the child
     *
     * @param childId childId
     * @param request request
     * @return location
     */
    private Location position(ChildId childId, IotRequest request, HttpServletRequest req) throws Exception {
        LocationType locationType = LocationType.valueOfValue(request.accesstype());

        Map map = new LinkedHashMap();
        map.put("accesstype", Integer.parseInt(request.accesstype()));
        map.put("imei", req.getAttribute("imei"));
        map.put("clientip", IpUtil.getClientIP(req));
        map.put("network", request.network());
        map.put("bts", request.bts());
        map.put("macs", request.macs());
        map.put("ctime", String.valueOf(Instant.now().toEpochMilli()));
        map.put("need_rgc", "Y");
        map.put("need_poi", "Y");
        map.put("sema", "aptagd");
        map.put("need_sub", "Y");
        map.put("cdma", 0);

        BaiduLocationResponse baiduLocationResponse = aMapBaiduRequestService.locate(map);
        if (Objects.isNull(baiduLocationResponse)) {
            return null;
        }

        AmapResponse.Position position = new AmapResponse.Position();
        position.setLocation(baiduLocationResponse.getLocation());
        position.setRadius(baiduLocationResponse.getRadius());

        AmapResponse.AddressComponent addressComponent = new AmapResponse.AddressComponent();
        addressComponent.setPoi(baiduLocationResponse.getPoi());
        addressComponent.setCity(baiduLocationResponse.getCity());
        addressComponent.setRoad(baiduLocationResponse.getRoad());
        addressComponent.setCountry(baiduLocationResponse.getCountry());
        addressComponent.setProvince(baiduLocationResponse.getProvince());
        addressComponent.setCityCode(baiduLocationResponse.getCitycode());
        addressComponent.setDistrict(baiduLocationResponse.getDistrict());
        addressComponent.setProvince(baiduLocationResponse.getProvince());
        addressComponent.setStreet(baiduLocationResponse.getRoad());
        addressComponent.setAdCode(baiduLocationResponse.getAdcode());
        String[] points = LocationUtil.splitPoint(position.getLocation());
        // 上海市 浦东新区 规划一支路 靠近陆家嘴软件园
        // city district  road  poi

        //百度定位不会AdCode，只有高德定位才会返回，所以当时百度定位时需要通过District(县区)
        String desc = "%s %s %s %s %s".formatted(addressComponent.getCountry(), addressComponent.getCity(), addressComponent.getDistrict(), addressComponent.getRoad(), addressComponent.getStreet());
        Address address = new Address().setLongitude(points[0]).setLatitude(points[1]).setRadius(position.getRadius()).setDesc(desc).setCountry(addressComponent.getCountry()).setProvince(addressComponent.getProvince()).setCity(addressComponent.getCity()).setCityCode(addressComponent.getCityCode()).setAdCode(addressComponent.getAdCode()).setRoad(addressComponent.getRoad()).setPoi(addressComponent.getPoi());
        Location location = new Location().setChannel(TrajectoryChannel.BAIDUMAPS).setType(locationType).setChildId(childId).setTraceId(request.traceId()).setAddress(address);
        log.info("baidumaps coordinates address: {}", location.getAddress());

        // Transform coordinates
        CoordinateConverter coordinateConverter = CoordinateConverter.getInstance(CoordinateConverter.defaultPoints());
        GlobalCoordinates coordinates = location.getCoordinates();
        location.setCoordinates(coordinateConverter.BD09toGCJ02(coordinates.getLongitude(), coordinates.getLatitude()));
        log.info("baidumaps transform coordinates after address: {}", location.getAddress());
        return location;
    }

    private Location gps(IotRequest request) {
        String lat = request.lat();
        String lon = request.lon();
        Long radius = request.radius();
        if (StringUtils.isBlank(lat) || StringUtils.isBlank(lon) || Objects.isNull(radius)) {
            throw new RuntimeException("请检查经纬度，精度是否是空值");
        }
        ChildId childId = ChildIdFactory.create(CurrentRequestUser.get().getId());
        LocationType locationType = LocationType.valueOfValue(request.accesstype());

        int radiusInt = radius.intValue();
        Address address = new Address().setLongitude(lon).setLatitude(lat).setRadius(radiusInt);
        Location location = new Location().setChannel(TrajectoryChannel.GPS).setType(locationType).setChildId(childId).setTraceId(request.traceId()).setAddress(address);
        log.info("gps coordinates address: {}", location.getAddress());

        // Transform coordinates
        CoordinateConverter coordinateConverter = CoordinateConverter.getInstance(CoordinateConverter.defaultPoints());
        GlobalCoordinates coordinates = location.getCoordinates();
        location.setCoordinates(coordinateConverter.WGS84toGCJ02(coordinates.getLongitude(), coordinates.getLatitude()));
        log.info("gps transform coordinates after address: {}", location.getAddress());
        return location;
    }

}
