package com.cmvr.web.controller.api;

import cmvr.msgs.Agv;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.model.agv.EdgeAgvMapNameVO;
import com.cmvr.edge.client.model.agv.EdgeAgvNavigateToPoseVO;
import com.cmvr.edge.client.model.agv.EdgeAgvNavigateToStationVO;
import com.cmvr.edge.client.model.agv.EdgeAgvStartMappingVO;
import com.cmvr.edge.client.model.agv.EdgeAgvUploadMapVO;
import com.cmvr.edge.client.model.agv.EdgeAgvVelocityVO;
import com.cmvr.edge.client.service.EdgeAgvService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 边缘系统 AGV 控制器。
 *
 * <p>接口入参约定：
 * GET 查询类接口使用 query 参数绑定 VO；POST 控制类接口只接收一个 JSON 请求体 VO，
 * 避免一个接口同时出现通用 VO 和零散业务参数，保证前端调用、Swagger 文档和后端绑定规则一致。</p>
 *
 * @author cmvr-iot
 * @since 2026-07-02
 */
@Api(tags = "边缘--AGV")
@RestController
@RequestMapping("/api/agv")
@RequiredArgsConstructor
public class EdgeAgvController {

    private final EdgeAgvService edgeAgvService;

    /**
     * 获取 AGV 运行时状态。
     */
    @ApiOperation("获取AGV运行时状态")
    @GetMapping("/getRuntimeState")
    public AjaxResult getRuntimeState(@Validated EdgeCommonVO vo) {
        Agv.AgvRuntimeState state = edgeAgvService.getRuntimeState(vo);
        return AjaxResult.ok(toRuntimeStateMap(state));
    }

    /**
     * 获取 AGV 当前导航状态。
     */
    @ApiOperation("获取导航状态")
    @GetMapping("/getNavigationStatus")
    public AjaxResult getNavigationStatus(@Validated EdgeCommonVO vo) {
        Agv.AgvNavigationStatus status = edgeAgvService.getNavigationStatus(vo);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("state", status.getState());
        resultMap.put("type", status.getType());
        resultMap.put("progress", status.getProgress());
        resultMap.put("message", status.getMessage());
        return AjaxResult.ok(resultMap);
    }

    /**
     * AGV 紧急停止。
     */
    @ApiOperation("紧急停止")
    @PostMapping("/emergencyStop")
    public AjaxResult emergencyStop(@RequestBody @Validated EdgeCommonVO vo) {
        edgeAgvService.emergencyStop(vo);
        return AjaxResult.ok();
    }

    /**
     * 清除 AGV 故障。
     */
    @ApiOperation("清除故障")
    @PostMapping("/clearFault")
    public AjaxResult clearFault(@RequestBody @Validated EdgeCommonVO vo) {
        edgeAgvService.clearFault(vo);
        return AjaxResult.ok();
    }

    /**
     * 导航到指定坐标。
     */
    @ApiOperation("导航到指定位置")
    @PostMapping("/navigateToPose")
    public AjaxResult navigateToPose(@RequestBody @Validated EdgeAgvNavigateToPoseVO vo) {
        Agv.AgvPose2d pose = Agv.AgvPose2d.newBuilder()
                .setX(vo.getX())
                .setY(vo.getY())
                .setTheta(vo.getTheta())
                .build();
        edgeAgvService.navigateToPose(vo, pose);
        return AjaxResult.ok();
    }

    /**
     * 导航到指定站点。
     */
    @ApiOperation("导航到站点")
    @PostMapping("/navigateToStation")
    public AjaxResult navigateToStation(@RequestBody @Validated EdgeAgvNavigateToStationVO vo) {
        edgeAgvService.navigateToStation(vo, vo.getStationId());
        return AjaxResult.ok();
    }

    /**
     * 暂停当前导航任务。
     */
    @ApiOperation("暂停导航")
    @PostMapping("/pauseNavigation")
    public AjaxResult pauseNavigation(@RequestBody @Validated EdgeCommonVO vo) {
        edgeAgvService.pauseNavigation(vo);
        return AjaxResult.ok();
    }

    /**
     * 恢复当前导航任务。
     */
    @ApiOperation("恢复导航")
    @PostMapping("/resumeNavigation")
    public AjaxResult resumeNavigation(@RequestBody @Validated EdgeCommonVO vo) {
        edgeAgvService.resumeNavigation(vo);
        return AjaxResult.ok();
    }

    /**
     * 取消当前导航任务。
     */
    @ApiOperation("取消导航")
    @PostMapping("/cancelNavigation")
    public AjaxResult cancelNavigation(@RequestBody @Validated EdgeCommonVO vo) {
        edgeAgvService.cancelNavigation(vo);
        return AjaxResult.ok();
    }

    /**
     * 设置 AGV 速度。
     */
    @ApiOperation("设置速度")
    @PostMapping("/setVelocity")
    public AjaxResult setVelocity(@RequestBody @Validated EdgeAgvVelocityVO vo) {
        Agv.AgvVelocity velocity = Agv.AgvVelocity.newBuilder()
                .setVx(vo.getVx())
                .setVy(vo.getVy())
                .setWz(vo.getWz())
                .build();
        edgeAgvService.setVelocity(vo, velocity);
        return AjaxResult.ok();
    }

    /**
     * 停止速度控制。
     */
    @ApiOperation("停止速度控制")
    @PostMapping("/stopVelocityControl")
    public AjaxResult stopVelocityControl(@RequestBody @Validated EdgeCommonVO vo) {
        edgeAgvService.stopVelocityControl(vo);
        return AjaxResult.ok();
    }

    /**
     * 查询机器人本地所有地图。
     */
    @ApiOperation("列出所有地图")
    @GetMapping("/listMaps")
    public AjaxResult listMaps(@Validated EdgeCommonVO vo) {
        List<String> maps = edgeAgvService.listMaps(vo);
        return AjaxResult.ok(maps);
    }

    /**
     * 查询机器人本地所有站点。
     */
    @ApiOperation("列出所有站点")
    @GetMapping("/listStations")
    public AjaxResult listStations(@Validated EdgeCommonVO vo) {
        List<Agv.AgvStation> stations = edgeAgvService.listStations(vo);
        List<Map<String, Object>> stationList = new ArrayList<>();
        for (Agv.AgvStation station : stations) {
            stationList.add(toStationMap(station));
        }
        return AjaxResult.ok(stationList);
    }

    /**
     * 切换机器人当前地图。
     */
    @ApiOperation("切换地图")
    @PostMapping("/switchMap")
    public AjaxResult switchMap(@RequestBody @Validated EdgeAgvMapNameVO vo) {
        edgeAgvService.switchMap(vo, vo.getMapName());
        return AjaxResult.ok();
    }

    /**
     * 上传地图内容到机器人。
     */
    @ApiOperation("上传地图")
    @PostMapping("/uploadMap")
    public AjaxResult uploadMap(@RequestBody @Validated EdgeAgvUploadMapVO vo) {
        edgeAgvService.uploadMap(vo, vo.getMapName(), vo.getContent());
        return AjaxResult.ok();
    }

    /**
     * 下载指定地图内容。
     */
    @ApiOperation("下载地图")
    @GetMapping("/downloadMap")
    public AjaxResult downloadMap(@Validated EdgeAgvMapNameVO vo) {
        String content = edgeAgvService.downloadMap(vo, vo.getMapName());
        return AjaxResult.ok(content);
    }

    /**
     * 开始建图。
     */
    @ApiOperation("开始建图")
    @PostMapping("/startMapping")
    public AjaxResult startMapping(@RequestBody @Validated EdgeAgvStartMappingVO vo) {
        Agv.AgvMapDimension mapDimension = Agv.AgvMapDimension.forNumber(vo.getDimension());
        if (mapDimension == null) {
            return AjaxResult.error("建图维度不合法，允许值：0=未指定，1=2D，2=3D，3=2D+3D");
        }

        String sessionId = edgeAgvService.startMapping(vo, mapDimension, vo.getMapName(), vo.isRealTime());
        Map<String, Object> result = new HashMap<>();
        result.put("sessionId", sessionId);
        return AjaxResult.ok(result);
    }

    /**
     * 停止建图。
     */
    @ApiOperation("停止建图")
    @PostMapping("/stopMapping")
    public AjaxResult stopMapping(@RequestBody @Validated EdgeCommonVO vo) {
        edgeAgvService.stopMapping(vo);
        return AjaxResult.ok();
    }

    /**
     * 将 protobuf 运行时状态转换为前端友好的 Map，避免直接暴露 protobuf 对象结构。
     */
    private Map<String, Object> toRuntimeStateMap(Agv.AgvRuntimeState state) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("timestamp", state.getTimestamp());
        resultMap.put("mode", state.getMode());
        resultMap.put("connected", state.getConnected());
        resultMap.put("localized", state.getLocalized());
        resultMap.put("moving", state.getMoving());
        resultMap.put("fault", state.getFault());
        resultMap.put("emergencyStopped", state.getEmergencyStopped());

        if (state.hasPose()) {
            resultMap.put("pose", toPoseMap(state.getPose()));
        }
        if (state.hasVelocity()) {
            Map<String, Object> velocityMap = new HashMap<>();
            velocityMap.put("vx", state.getVelocity().getVx());
            velocityMap.put("vy", state.getVelocity().getVy());
            velocityMap.put("wz", state.getVelocity().getWz());
            resultMap.put("velocity", velocityMap);
        }
        if (state.hasBattery()) {
            Map<String, Object> batteryMap = new HashMap<>();
            batteryMap.put("percentage", state.getBattery().getPercentage());
            batteryMap.put("voltage", state.getBattery().getVoltage());
            batteryMap.put("current", state.getBattery().getCurrent());
            batteryMap.put("temperature", state.getBattery().getTemperature());
            batteryMap.put("charging", state.getBattery().getCharging());
            resultMap.put("battery", batteryMap);
        }

        resultMap.put("currentMap", state.getCurrentMap());
        resultMap.put("currentStation", state.getCurrentStation());
        resultMap.put("lastError", state.getLastError());
        return resultMap;
    }

    /**
     * 将 protobuf 站点对象转换为普通 Map。
     */
    private Map<String, Object> toStationMap(Agv.AgvStation station) {
        Map<String, Object> stationMap = new HashMap<>();
        stationMap.put("id", station.getId());
        stationMap.put("type", station.getType());
        stationMap.put("description", station.getDescription());
        if (station.hasPose()) {
            stationMap.put("pose", toPoseMap(station.getPose()));
        }
        return stationMap;
    }

    /**
     * 将 protobuf 二维位姿转换为普通 Map。
     */
    private Map<String, Object> toPoseMap(Agv.AgvPose2d pose) {
        Map<String, Object> poseMap = new HashMap<>();
        poseMap.put("x", pose.getX());
        poseMap.put("y", pose.getY());
        poseMap.put("theta", pose.getTheta());
        return poseMap;
    }
}
