package com.fuyuanshen.equipment.service.impl;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.TrackService;
import com.fuyuanshen.equipment.domain.bo.TerminalDelBo;
import com.fuyuanshen.equipment.domain.bo.TerminalDeviceBo;
import com.fuyuanshen.equipment.domain.bo.TerminalQueryBo;
import com.fuyuanshen.equipment.domain.bo.TrackServiceBo;
import com.fuyuanshen.equipment.domain.dto.TerminalDeviceDto;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.domain.vo.TrackServiceVo;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.TrackServiceMapper;
import com.fuyuanshen.equipment.service.ITrackServiceService;
import com.fuyuanshen.equipment.utils.map.AmapTrackUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 轨迹服务Service业务层处理
 *
 * @author Lion Li
 * @date 2025-11-24
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class TrackServiceServiceImpl implements ITrackServiceService {

    private final TrackServiceMapper baseMapper;
    private final DeviceMapper deviceMapper;
    private final AmapTrackUtil amapTrackUtil;

    /**
     * 查询轨迹服务
     *
     * @param id 主键
     * @return 轨迹服务
     */
    @Override
    public TrackServiceVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询轨迹服务列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 轨迹服务分页列表
     */
    @Override
    public TableDataInfo<TrackServiceVo> queryPageList(TrackServiceBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TrackService> lqw = buildQueryWrapper(bo);
        Page<TrackServiceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的轨迹服务列表
     *
     * @param bo 查询条件
     * @return 轨迹服务列表
     */
    @Override
    public List<TrackServiceVo> queryList(TrackServiceBo bo) {
        LambdaQueryWrapper<TrackService> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<TrackService> buildQueryWrapper(TrackServiceBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<TrackService> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(TrackService::getId);
        lqw.eq(bo.getSid() != null, TrackService::getSid, bo.getSid());
        lqw.like(StringUtils.isNotBlank(bo.getSname()), TrackService::getSname, bo.getSname());
        return lqw;
    }

    /**
     * 新增轨迹服务
     *
     * @param bo 轨迹服务
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(TrackServiceBo bo) {
        TrackService add = MapstructUtils.convert(bo, TrackService.class);
        validEntityBeforeSave(add);
        /* 1. 先查高德侧是否已存在同名服务 */
        JSONObject listResult = amapTrackUtil.listService();
        if (listResult == null || listResult.getInt("errcode") != 10000) {
            throw new RuntimeException("查询高德轨迹服务列表失败");
        }
        log.warn("查询: {}", listResult);
        String serviceName = add.getSname();
        String sid = null;

        // 取 data 数组，遍历匹配 name
        JSONArray serviceArray = listResult.getByPath("data.results", JSONArray.class);
        if (serviceArray != null) {
            for (int i = 0; i < serviceArray.size(); i++) {
                JSONObject item = serviceArray.getJSONObject(i);
                if (serviceName.equals(item.getStr("name"))) {
                    sid = item.getStr("sid");   // 已存在，直接复用
                    break;
                }
            }
        }

        /* 2. 不存在才创建 */
        if (sid == null) {
            JSONObject createResult = amapTrackUtil.createService(serviceName);
            if (createResult == null || createResult.getInt("errcode") != 10000) {
                throw new RuntimeException("生成高德轨迹服务失败");
            }
            sid = createResult.getByPath("data.sid", String.class);
        }

        add.setSid(Long.valueOf(sid));
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改轨迹服务
     *
     * @param bo 轨迹服务
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(TrackServiceBo bo) {
        TrackService update = MapstructUtils.convert(bo, TrackService.class);
        validEntityBeforeSave(update);
        if(update.getId() == null) {
            throw new RuntimeException("轨迹服务ID不能为空");
        }

        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(TrackService entity){
        if(entity == null) {
            throw new RuntimeException("请输入信息");
        }
        //TODO 做一些数据校验,如唯一约束
        if(!StringUtils.isNotEmpty(entity.getSname())) {
            throw new RuntimeException("轨迹服务名称不能为空");
        }
    }

    /**
     * 校验并批量删除轨迹服务信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delTerminalDevice(Collection<Long> ids, Boolean isValid) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new IllegalArgumentException("请选择要移除的设备");
        }

        // 1. 批量查询（一次 SQL，性能提升 10 倍+）
        List<Device> deviceList = deviceMapper.selectList(
                new LambdaQueryWrapper<Device>()
                        .in(Device::getId, ids)
                        .select(Device::getId, Device::getSid, Device::getTid, Device::getTrid)
        );

        if (CollectionUtils.isEmpty(deviceList)) {
            log.info("未找到要移除的设备，ids={}", ids);
            return true; // 幂等：没找到也算成功
        }

        // 2. 业务校验（可插拔）
        if (isValid) {
            validateBeforeDelete(deviceList); // 你自己实现具体校验逻辑
        }

        // 3. 批量清理高德资源 + 本地数据（精准对应，顺序正确）
        for (Device device : deviceList) {
            Long sid = device.getSid();
            Long tid = device.getTid();
            Long trid = device.getTrid();

            if (sid == null) {
                log.warn("设备无高德服务ID，跳过高德清理，deviceId={}", device.getId());
                continue;
            }

            // 先删轨迹（必须 tid + trid），再删终端（只需 tid）
            if (trid != null && tid != null) {
                safeDeleteTrack(sid, tid, trid);
            }
            if (tid != null) {
                safeDeleteTerminal(sid, tid);
            }
        }

        // 4. 批量更新数据库（性能极高，避免 N+1 update）
        int updated = deviceMapper.update(null,
                new LambdaUpdateWrapper<Device>()
                        .in(Device::getId, ids)
                        .set(Device::getTid, null)
                        .set(Device::getTrid, null)
                        .set(Device::getSid, null)
        );

        log.info("成功解绑高德终端，设备数量={}，实际更新记录数={}", deviceList.size(), updated);
        return true;
    }

    @Override
    public Boolean delTerminalDevice(TerminalDelBo bo, Boolean isValid) {
        var list = deviceMapper.selectList(new LambdaQueryWrapper<Device>()
                .eq(Device::getSid, bo.getSid())
                .eq(Device::getTid, bo.getTid()));
        if (list.isEmpty()) {
            throw new IllegalArgumentException("本地设备已绑定不允许删除");
        }
        // 先删轨迹（必须 tid + trid），再删终端（只需 tid）
        if (bo.getSid() != null && bo.getTid() != null) {
            safeDeleteTrack(bo.getSid(), bo.getTid(), bo.getTrid());
        }
        if (bo.getTid() != null) {
            safeDeleteTerminal(bo.getSid(),bo.getTid());
        }
        return true;
    }

    /**
     * 查询终端列表
     *
     * @param bo 查询参数
     * @return 终端列表
     */
    @Override
    public TableDataInfo<TerminalDeviceDto> listTerminal(TerminalQueryBo bo) {
        JSONObject terminalRes = amapTrackUtil.listTerminal(bo.getSid(), bo.getTid(), bo.getName(), bo.getPage());
        List<TerminalDeviceDto> list =  terminalRes.getByPath("data.results", JSONArray.class)
                .toList(TerminalDeviceDto.class);
        Integer count = terminalRes.getByPath("data.count", Integer.class);
        return new TableDataInfo<TerminalDeviceDto>(list, count);
    }

    /**
     * 搜索终端列表
     *
     * @param bo 搜索参数
     * @return 终端列表
     */
    @Override
    public TableDataInfo<TerminalDeviceDto> searchTerminal(TerminalQueryBo bo) {
        JSONObject terminalRes = amapTrackUtil.searchTerminal(bo.getSid(), bo.getKeywords(), bo.getPage(), bo.getPagesize());
        log.info("终端列表：{}", terminalRes);
        List<TerminalDeviceDto> list = terminalRes.getByPath("data.results", JSONArray.class)
                .toList(TerminalDeviceDto.class);
        Integer count = terminalRes.getByPath("data.count", Integer.class);
        return new TableDataInfo<TerminalDeviceDto>(list, count);
    }

    /**
     * 业务校验钩子
     */
    private void validateBeforeDelete(List<Device> deviceList) {
        // 示例：校验设备是否在线、是否有未完成订单等
        // throw new BusinessException("xxx设备正在使用中，不能解绑");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void terminal(TerminalDeviceBo bo) {
        if (bo.getSid() == null) {
            throw new IllegalArgumentException("高德服务ID不能为空");
        }
        if (CollectionUtils.isEmpty(bo.getDeviceList())) {
            throw new IllegalArgumentException("请选择设备");
        }

        Long sid = bo.getSid();

        // 使用对象封装，便于补偿时知道 tid 和 trid 的对应关系（关键！）
        class CreatedResource {
            Long tid;
            Long trid;
        }
        List<CreatedResource> createdResources = new ArrayList<>();

        try {
            for (DeviceForm form : bo.getDeviceList()) {
                String terminalName = String.format("%s_%s", form.getTypeName(), form.getDeviceImei());

                // Step 1: 创建终端
                JSONObject terminalRes = amapTrackUtil.createTerminal(sid, terminalName);
                if (!isSuccess(terminalRes)) {
                    throw new RuntimeException("创建设备终端失败：" + terminalRes);
                }
                Long tid = terminalRes.getByPath("data.tid", Long.class);

                // Step 2: 创建轨迹
                JSONObject trackRes = amapTrackUtil.createTrace(sid, tid);
                if (!isSuccess(trackRes)) {
                    throw new RuntimeException("创建轨迹失败：" + trackRes);
                }
                Long trid = trackRes.getByPath("data.trid", Long.class);

                // 3. 更新本地设备
                Device device = new Device();
                device.setId(form.getId());
                device.setTid(tid);
                device.setTrid(trid);
                device.setSid(sid);
                deviceMapper.updateById(device);

                // 记录本次创建的资源（用于精确补偿）
                CreatedResource resource = new CreatedResource();
                resource.tid = tid;
                resource.trid = trid;
                createdResources.add(resource);

                // 可落盘日志（用于极端情况人工排查）
                log.warn("高德添加设备终端 sid={} tid={} trid={} deviceId={}",
                        sid, tid, trid, form.getId());
            }

            // 全部成功 → 清空内存集合
            createdResources.clear();

        } catch (Exception e) {
            log.warn("绑定高德终端失败，正在执行补偿清理，已创建资源数={}", createdResources.size(), e);

            // 精确补偿：先删轨迹（需要 sid + tid + trid），再删终端（需要 sid + tid）
            for (CreatedResource r : createdResources) {
                if (r.trid != null) {
                    safeDeleteTrack(sid, r.tid, r.trid);  // 现在传 3 个参数
                }
                if (r.tid != null) {
                    safeDeleteTerminal(sid, r.tid);
                }
            }

            throw e; // 抛异常让调用方知道失败
        }
    }

    /** 安全删除轨迹 */
    private void safeDeleteTrack(Long sid, Long tid, Long trid) {
        try {
            // 假设你的工具方法是这样定义的：
            // amapTrackUtil.deleteTrack(sid, tid, trid);
            amapTrackUtil.deleteTrace(sid, tid, trid);
            log.info("补偿删除轨迹成功 sid={} tid={} trid={}", sid, tid, trid);
        } catch (Exception ex) {
            log.error("补偿删除轨迹失败 sid={} tid={} trid={}，请人工介入或等定时任务处理", sid, tid, trid, ex);
        }
    }

    /** 安全删除终端 */
    private void safeDeleteTerminal(Long sid, Long tid) {
        try {
            amapTrackUtil.deleteTerminal(sid, tid);
            log.info("补偿删除终端成功 sid={} tid={}", sid, tid);
        } catch (Exception ex) {
            log.error("补偿删除终端失败 sid={} tid={}，请人工介入或等定时任务处理", sid, tid, ex);
        }
    }

    private boolean isSuccess(JSONObject json) {
        return json != null && Objects.equals(json.getInt("errcode"), 10000);
    }
}