package bus.service.impl;

import bus.model.SnowflakeUtil;
import bus.model.dto.*;
import bus.model.dto.page.BBusInfoPageDto;
import bus.model.dto.req.BTrackLocalReqDto;
import bus.model.po.*;
import bus.mapper.BBusInfoMapper;

import bus.service.*;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.orcas.common.sso.model.ExtInfo;
import com.orcas.iso.config.common.user.CurrentUserHolder;
import com.qzwisdom.qzframework.core.tool.exception.BusinessException;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.BeanUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Program: bus
 * @Description: 描述
 * @Author: zy
 * @Date: 2025-03-01 09:44:24
 **/
@Service
public class BBusInfoServiceImpl extends ServiceImpl<BBusInfoMapper,BBusInfoPo> implements BBusInfoService {

    @Autowired
    private BBusInfoMapper bBusInfoMapper;

    @Autowired
    private BCourseBusService bCourseBusService;
    @Resource
    private SnowflakeUtil snowflakeUtil;
    @Resource
    BCourseInfoService bCourseInfoService;
    @Resource
    BCourseStationService bCourseStationService;
    @Resource
    BStationInfoService bStationInfoService;

	/**
     * 详情
     * @param id
     * @return
     */
    @Override
    public BBusInfoDto getDetailById(String id){
        BBusInfoPo bBusInfoPo = this.bBusInfoMapper.selectById(id);
        BBusInfoDto bBusInfoDto = BeanUtil.toBean(bBusInfoPo, BBusInfoDto.class);
        return bBusInfoDto;
    }

	/**
     * 保存
     * @param dto
     * @return
     */
    @Override
	public void save(BBusInfoDto dto){
        BBusInfoPo existingBus = this.bBusInfoMapper.selectOne(
                new QueryWrapper<BBusInfoPo>()
                        .eq("is_delete",0)
                        .eq("bus_no", dto.getBusNo())
        );
        if (existingBus != null) {
            throw new BusinessException("车牌号已存在，请检查后重试");
        }
        BBusInfoPo po = new BBusInfoPo();
        BeanUtils.copyProperties(dto,po);
        String driverPhone = po.getDriverPhone();
        boolean phone = PhoneUtil.isPhone(driverPhone);
        if(!phone){
            throw new BusinessException("手机号格式不正确");
        }
        ExtInfo extInfo = CurrentUserHolder.get();
        po.setId(snowflakeUtil.snowflakeId());
        po.setCreateTime(new Date());
        po.setUpdateTime(new Date());
        po.setCreatorId(extInfo.getUserId());
        po.setUpdaterId(extInfo.getUserId());
        po.setCreatorName(extInfo.getUserName());
        po.setUpdaterName(extInfo.getUserName());
		this.bBusInfoMapper.insert(po);
    }

    /**
    * 列表
    * @param dto
    * @return
    */
    @Override
    public List<BBusInfoDto> list(BBusInfoPageDto dto){
        return this.bBusInfoMapper.list(dto);
    }

	/**
     * 修改
     * @param dto
     * @return
     */
    @Override
    public void update(BBusInfoDto dto){
        BBusInfoPo existingBus = this.bBusInfoMapper.selectOne(
                new QueryWrapper<BBusInfoPo>()
                        .eq("bus_no", dto.getBusNo())
                        .eq("is_delete",0)
                        .ne("id", dto.getId())
        );
        if (existingBus != null) {
            throw new BusinessException("车牌号已存在，请检查后重试");
        }
        BBusInfoPo po = new BBusInfoPo();
        BeanUtils.copyProperties(dto,po);
        String driverPhone = po.getDriverPhone();
        boolean phone = PhoneUtil.isPhone(driverPhone);
        if(!phone){
            throw new BusinessException("手机号格式不正确");
        }
        ExtInfo extInfo = CurrentUserHolder.get();
        po.setUpdateTime(new Date());
        po.setUpdaterId(extInfo.getUserId());
        po.setUpdaterName(extInfo.getUserName());
		bBusInfoMapper.updateById(po);
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @Override
    public void delete(String id){
		bBusInfoMapper.deleteById(id);
    }

    /**
     * 获取车辆状态树型数据
     *
     * @return
     */
    @Override
    public List<BBusTrackTreeDto> getTreeList() {
        // 查询所有未删除的车辆信息
        List<BBusInfoPo> busList = this.bBusInfoMapper.selectList(
                new QueryWrapper<BBusInfoPo>()
                        .eq("is_delete", 0)
        );
        // 按状态分组
        Map<String, List<BBusInfoPo>> busGroups = busList.stream()
                .collect(Collectors.groupingBy(BBusInfoPo::getStatus));

        // 构建树形结构
        List<BBusTrackTreeDto> treeList = new ArrayList<>();

        // 添加状态分组节点
        busGroups.forEach((status, buses) -> {
            BBusTrackTreeDto statusNode = new BBusTrackTreeDto();
            statusNode.setStatus(status);
            // 添加子节点（车辆）
            List<BTrackNameDto> children = buses.stream()
                    .map(bus -> {
                        BTrackNameDto busNode = new BTrackNameDto();
                        busNode.setId(bus.getId());
                        busNode.setName(bus.getBusName());
                        busNode.setCode(bus.getBusNo());
                        return busNode;
                    })
                    .collect(Collectors.toList());

            statusNode.setList(children);
            treeList.add(statusNode);
        });

        return treeList;
    }

    /**
     * 根据车辆ID获取车辆追踪信息
     *
     * @param busId
     * @return
     */
    @Override
    public BBusTrackInfoDto getTrackInfo(String busId) {
        BBusTrackInfoDto dto = new BBusTrackInfoDto();
        // 1. 查询车辆所在路线，获取最新分配的路线
        QueryWrapper<BCourseBusPo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("bus_id", busId)
                .eq("is_delete", 0)
                .orderByDesc("create_time")  // 按创建时间倒序
                .last("LIMIT 1");  // 只取最新的一条记录
        BCourseBusPo courseBus = bCourseBusService.getOne(queryWrapper);
        if (courseBus == null) {
            throw new BusinessException("该车辆未分配路线");
        }

        // 2. 获取路线信息
        String courseId = courseBus.getCourseId();
        BCourseInfoPo courseInfo = bCourseInfoService.getById(courseId);
        if (courseInfo == null) {
            throw new BusinessException("该路线不存在");
        }

        // 3. 获取车辆信息
        BBusInfoPo busInfo = this.getById(busId);
        if (busInfo == null) {
            throw new BusinessException("车辆不存在");
        }

        // 4. 设置基本信息
        dto.setBusId(busId);
        dto.setBusNo(busInfo.getBusNo());
        dto.setCourseName(courseInfo.getCourseName());
        dto.setFirstStationName(courseInfo.getFirstStationName());
        dto.setLastStationName(courseInfo.getLastStationName());

        // 5. 设置车辆追踪信息
        BBusTrackByBusDto trackByBusDto = new BBusTrackByBusDto();
        trackByBusDto.setDriverName(busInfo.getDriverName());
        trackByBusDto.setDriverPhone(busInfo.getDriverPhone());
        trackByBusDto.setStatus(busInfo.getStatus());
        trackByBusDto.setCourseName(courseInfo.getCourseName());
        // TODO: 这里需要集成实时定位服务来获取车辆的当前位置信息
        dto.setBBusTrackByBusDto(trackByBusDto);

        // 6. 获取路线站点信息
        List<BCourseStationPo> stationList = bCourseStationService.getListByCourse(courseId);
        if (CollectionUtil.isNotEmpty(stationList)) {
            List<BCourseNameInfoDto> courseNameInfoList = new ArrayList<>();
            List<String> stationIds = stationList.stream()
                    .map(BCourseStationPo::getStationId)
                    .collect(Collectors.toList());

            bStationInfoService.listByIds(stationIds).forEach(stationInfo -> {
                BCourseNameInfoDto courseNameInfo = new BCourseNameInfoDto();
                courseNameInfo.setId(stationInfo.getId());
                List<BStationPointDto> pointList = new ArrayList<>();
                BStationPointDto point = new BStationPointDto();
                point.setId(stationInfo.getId());
                point.setName(stationInfo.getName());
                point.setLatitude(stationInfo.getLatitude());
                point.setLongitude(stationInfo.getLongitude());
                pointList.add(point);
                courseNameInfo.setSlist(pointList);
                courseNameInfoList.add(courseNameInfo);
            });

            dto.setBCourseNameInfoList(courseNameInfoList);
        }

        return dto;
    }

    /**
     * 根据车辆ID获取车辆实时追踪信息
     * @param bTrackLocalReqDto 车辆位置请求信息
     * @return 车辆位置和相邻站点信息
     */
    @Override
    public BTrackLocalDto getLocalBusInfo(BTrackLocalReqDto bTrackLocalReqDto) {
        // 1. 根据车牌号获取车辆信息
        QueryWrapper<BBusInfoPo> busQuery = new QueryWrapper<>();
        busQuery.eq("bus_no", bTrackLocalReqDto.getBusNo())
                .eq("is_delete", 0);
        BBusInfoPo busInfo = this.getOne(busQuery);
        if (busInfo == null) {
            throw new BusinessException("车辆不存在");
        }

        // 2. 获取车辆所在路线
        QueryWrapper<BCourseBusPo> courseQuery = new QueryWrapper<>();
        courseQuery.eq("bus_id", busInfo.getId())
                .eq("is_delete", 0)
                .orderByDesc("create_time")
                .last("LIMIT 1");
        BCourseBusPo courseBus = bCourseBusService.getOne(courseQuery);
        if (courseBus == null) {
            throw new BusinessException("该车辆未分配路线");
        }

        // 3. 获取路线所有站点
        List<BCourseStationPo> stationList = bCourseStationService.getListByCourse(courseBus.getCourseId());
        if (CollectionUtil.isEmpty(stationList)) {
            throw new BusinessException("路线站点信息不存在");
        }

        // 4. 获取所有站点信息
        List<String> stationIds = stationList.stream()
                .map(BCourseStationPo::getStationId)
                .collect(Collectors.toList());
        List<BStationInfoPo> stations = bStationInfoService.listByIds(stationIds);

        // 按路线顺序排序站点
        Map<String, BStationInfoPo> stationMap = stations.stream()
                .collect(Collectors.toMap(BStationInfoPo::getId, station -> station));
        List<BStationInfoPo> orderedStations = stationList.stream()
                .map(station -> stationMap.get(station.getStationId()))
                .collect(Collectors.toList());

        // 5. 找出车辆当前位置的前后站点
        BStationInfoPo prevStation = null;
        BStationInfoPo nextStation = null;
        double minDistance = Double.MAX_VALUE;
        int currentIndex = -1;

        for (int i = 0; i < orderedStations.size() - 1; i++) {
            BStationInfoPo station1 = orderedStations.get(i);
            BStationInfoPo station2 = orderedStations.get(i + 1);

            // 计算车辆到这两个站点的距离
            double dist1 = calculateDistance(
                bTrackLocalReqDto.getLocalLatitude().doubleValue(),
                bTrackLocalReqDto.getLocalLongitude().doubleValue(),
                station1.getLatitude().doubleValue(),
                station1.getLongitude().doubleValue()
            );
            double dist2 = calculateDistance(
                bTrackLocalReqDto.getLocalLatitude().doubleValue(),
                bTrackLocalReqDto.getLocalLongitude().doubleValue(),
                station2.getLatitude().doubleValue(),
                station2.getLongitude().doubleValue()
            );

            // 计算车辆是否在这两个站点之间
            // 使用三角形不等式判断：如果车辆在两站点之间，那么到两站点的距离之和应该接近两站点之间的距离
            double stationDistance = calculateDistance(
                station1.getLatitude().doubleValue(),
                station1.getLongitude().doubleValue(),
                station2.getLatitude().doubleValue(),
                station2.getLongitude().doubleValue()
            );

            double totalDistance = dist1 + dist2;
            // 允许5%的误差
            if (Math.abs(totalDistance - stationDistance) < stationDistance * 0.05
                    && totalDistance < minDistance) {
                minDistance = totalDistance;
                prevStation = station1;
                nextStation = station2;
                currentIndex = i;
            }
        }

        // 6. 构建返回数据
        BTrackLocalDto result = new BTrackLocalDto();

        // 设置当前位置信息
        result.setLocalLatitude(bTrackLocalReqDto.getLocalLatitude());
        result.setLocalLongitude(bTrackLocalReqDto.getLocalLongitude());
        result.setLocalSpeed(bTrackLocalReqDto.getLocalSpeed());

        if (prevStation != null && nextStation != null) {
            // 设置站点信息
            result.setLastStationId(prevStation.getId());
            result.setLastStationName(prevStation.getName());
            result.setNextStationId(nextStation.getId());
            result.setNextStationName(nextStation.getName());

            // 计算到下一站的距离（米）
            double distanceToNext = calculateDistance(
                bTrackLocalReqDto.getLocalLatitude().doubleValue(),
                bTrackLocalReqDto.getLocalLongitude().doubleValue(),
                nextStation.getLatitude().doubleValue(),
                nextStation.getLongitude().doubleValue()
            );

            // 设置到下一站的距离和预计时间
            result.setToNextStationDistance(String.format("%.0f", distanceToNext));

            // 如果有速度信息，计算预计到达时间
            if (bTrackLocalReqDto.getLocalSpeed() != null && bTrackLocalReqDto.getLocalSpeed().compareTo(BigDecimal.ZERO) > 0) {
                // 速度转换为米/秒
                double speedMps = bTrackLocalReqDto.getLocalSpeed().doubleValue() * 1000 / 3600;
                // 计算预计时间（秒）
                int estimatedSeconds = (int) (distanceToNext / speedMps);
                // 转换为分钟
                int estimatedMinutes = Math.max(1, estimatedSeconds / 60);
                result.setToNextStationTime(String.format("%d", estimatedMinutes));
            }

            // 设置行驶方向（0表示向下，1表示向上）
            result.setBusDirection("0"); // 因为我们是按照路线顺序遍历的，所以一定是向下
        } else {
            // 如果没有找到合适的站点对，找最近的站点
            BStationInfoPo nearestStation = findNearestStation(
                bTrackLocalReqDto.getLocalLatitude().doubleValue(),
                bTrackLocalReqDto.getLocalLongitude().doubleValue(),
                orderedStations
            );
            if (nearestStation != null) {
                result.setLastStationId(nearestStation.getId());
                result.setNextStationName(nearestStation.getName());
                // 设置一个默认距离
                result.setToNextStationDistance("未知");
                result.setToNextStationTime("未知");
            }
        }

        return result;
    }

    @Override
    public List<BBusInfoPo> getAllPoint() {
        QueryWrapper<BBusInfoPo> queryWrapper = new QueryWrapper<>();
        List<BBusInfoPo> bBusInfoPos = bBusInfoMapper.selectList(queryWrapper);
        return bBusInfoPos;
    }

    /**
     * 找出最近的站点
     */
    private BStationInfoPo findNearestStation(double lat, double lon, List<BStationInfoPo> stations) {
        if (CollectionUtil.isEmpty(stations)) {
            return null;
        }
        
        BStationInfoPo nearest = null;
        double minDistance = Double.MAX_VALUE;
        
        for (BStationInfoPo station : stations) {
            double distance = calculateDistance(
                lat, lon,
                station.getLatitude().doubleValue(),
                station.getLongitude().doubleValue()
            );
            if (distance < minDistance) {
                minDistance = distance;
                nearest = station;
            }
        }
        
        return nearest;
    }

    @Override
    public BTrackStationDto getPoint(String busId) {
        BTrackStationDto dto = new BTrackStationDto();
        QueryWrapper<BCourseBusPo> queryWrapper = new QueryWrapper();
        queryWrapper.eq("bus_id", busId);
        queryWrapper.eq("is_delete", 0);
        BCourseBusPo courseBus = (BCourseBusPo)this.bCourseBusService.getOne(queryWrapper);
        if (courseBus == null) {
            throw new BusinessException("该车辆未分配路线");
        } else {
            String courseId = courseBus.getCourseId();
            BCourseInfoPo byId = (BCourseInfoPo)this.bCourseInfoService.getById(courseId);
            if (byId == null) {
                throw new BusinessException("该路线不存在");
            } else {
                List<BCourseStationPo> listByCourse = this.bCourseStationService.getListByCourse(courseId);
                if (CollectionUtil.isNotEmpty(listByCourse)) {
                    List<String> stationIds = (List)listByCourse.stream().map(BCourseStationPo::getStationId).collect(Collectors.toList());
                    List<BStationPointDto> list = new ArrayList();
                    this.bStationInfoService.listByIds(stationIds).forEach((bStationInfoPo) -> {
                        BStationPointDto bCoursePointDto = new BStationPointDto();
                        bCoursePointDto.setLatitude(bCoursePointDto.getLatitude());
                        bCoursePointDto.setLongitude(bCoursePointDto.getLongitude());
                        bCoursePointDto.setId(bStationInfoPo.getId());
                        bCoursePointDto.setName(bStationInfoPo.getName());
                        list.add(bCoursePointDto);
                    });
                    dto.setSlist(list);
                }

                String mapPoint = byId.getMapPoint();
                if (StrUtil.isNotBlank(mapPoint)) {
                    List<BCoursePointDto> fileJsons = JSONUtil.toList(mapPoint, BCoursePointDto.class);
                    dto.setClist(fileJsons);
                }

                dto.setId(busId);
                return dto;
            }
        }
    }

    /**
     * 计算两个地理坐标点之间的距离（米）
     * 使用球面余弦定理计算，计算量更少，对于城市内短距离计算精度足够
     * @param lat1 第一个点的纬度
     * @param lon1 第一个点的经度
     * @param lat2 第二个点的纬度
     * @param lon2 第二个点的经度
     * @return 两点之间的距离，单位：米
     */
    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        // 将经纬度转换为弧度
        lat1 = Math.toRadians(lat1);
        lon1 = Math.toRadians(lon1);
        lat2 = Math.toRadians(lat2);
        lon2 = Math.toRadians(lon2);

        // 使用球面余弦定理计算
        double distance = Math.acos(
            Math.sin(lat1) * Math.sin(lat2) +
            Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)
        ) * 6371000; // 6371000是地球平均半径（米）

        return distance;
    }
}
