package com.ruoyi.web.controller.robot;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.robot.domain.LdMapProject;
import com.ruoyi.robot.domain.LdRoadmap;
import com.ruoyi.robot.domain.vo.LdMapListVo;
import com.ruoyi.robot.service.ILdMapProjectService;
import com.ruoyi.robot.service.ILdRoadmapService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * LD导航系统HTTP接口
 * 提供地图工程、瓦片地图、路网矢量地图相关接口
 *
 * 接口前缀: /v1
 */
@Api(tags = "LD导航系统HTTP接口")
@RestController
@RequestMapping("/v1")
public class LdNavController {

    private static final Logger log = LoggerFactory.getLogger(LdNavController.class);

    @Autowired
    private ILdMapProjectService mapProjectService;

    @Autowired
    private ILdRoadmapService roadmapService;

    @Autowired
    private RedisCache redisCache;

    @Value("${ld-nav.url:http://192.168.0.102:8086}")
    private String ldNavUrl;

    @Value("${tilemap.base-url:http://192.168.0.30}")
    private String tilemapBaseUrl;

    private final RestTemplate restTemplate = new RestTemplate();

    // config.json 缓存，按地图名索引
    private final ConcurrentHashMap<String, AtomicReference<JSONObject>> configCache = new ConcurrentHashMap<>();

    // ==================== 3.1 地图工程接口 ====================

    /**
     * 3.1.1 获取地图列表
     * GET /v1/map/list
     */
    @ApiOperation("获取地图列表")
    @GetMapping("/map/list")
    public ResponseEntity<Map<String, Object>> getMapList() {
        try {
            // 先从本地数据库获取
            List<LdMapListVo> mapList = mapProjectService.selectMapList();

            if (mapList != null && !mapList.isEmpty()) {
                Map<String, Object> response = new HashMap<>();
                response.put("status", true);

                List<String> maps = new java.util.ArrayList<>();
                List<String> states = new java.util.ArrayList<>();
                for (LdMapListVo vo : mapList) {
                    maps.add(vo.getMapName());
                    states.add(vo.getStatus());
                }
                response.put("maps", maps);
                response.put("states", states);

                return ResponseEntity.ok(response);
            }

            // 返回空列表
            Map<String, Object> response = new HashMap<>();
            response.put("status", true);
            response.put("maps", new java.util.ArrayList<>());
            response.put("states", new java.util.ArrayList<>());
            return ResponseEntity.ok(response);

        } catch (Exception e) {
            log.error("获取地图列表异常", e);
            return buildErrorResponse(4, "内部错误: " + e.getMessage());
        }
    }

    /**
     * 3.1.2 获取当前地图
     * GET /v1/map/using
     */
    @ApiOperation("获取当前使用中的地图")
    @GetMapping("/map/using")
    public ResponseEntity<Map<String, Object>> getCurrentMap(
            @ApiParam("设备ID") @RequestParam(required = false) String deviceId) {
        try {
            // 尝试从本地数据库获取
            LdMapProject currentMap = mapProjectService.selectCurrentMap(deviceId);

            if (currentMap != null) {
                Map<String, Object> response = new HashMap<>();
                response.put("status", true);
                response.put("name", currentMap.getMapName());
                return ResponseEntity.ok(response);
            }

            // 如果本地没有数据，尝试从LD导航系统获取
            String url = ldNavUrl + "/v1/map/using";
            if (StringUtils.isNotBlank(deviceId)) {
                url += "?deviceId=" + deviceId;
            }
            ResponseEntity<String> ldResponse = restTemplate.getForEntity(url, String.class);

            if (ldResponse.getStatusCode() == HttpStatus.OK && ldResponse.getBody() != null) {
                return ResponseEntity.ok(JSON.parseObject(ldResponse.getBody()));
            }

            return buildErrorResponse(2, "未找到当前使用中的地图");

        } catch (Exception e) {
            log.error("获取当前地图异常", e);
            return buildErrorResponse(4, "内部错误: " + e.getMessage());
        }
    }

    /**
     * 3.1.3 获取地图缩略图
     * GET /v1/map/thumbnail?map=地图名
     */
    @ApiOperation("获取地图缩略图")
    @GetMapping("/map/thumbnail")
    public R getMapThumbnail(@ApiParam("地图名称") @RequestParam("map") String mapName) {
        if (StringUtils.isBlank(mapName)) {
            log.warn("获取地图缩略图失败：mapName 为空");
            return R.fail("获取地图缩略图失败");
        }

        try {
            String thumbnailUrl = mapProjectService.getThumbnailUrl(mapName);

            if (StringUtils.isBlank(thumbnailUrl)) {
                log.info("地图 {} 的缩略图为空", mapName);
                return R.fail("地图缩略图为空");
            }

            return R.ok(tilemapBaseUrl + thumbnailUrl);
        } catch (Exception e) {
            log.error("获取地图 {} 缩略图异常", mapName, e);
            return R.fail("获取地图缩略图异常");
        }
    }

    /**
     * 3.1.4 地图重命名
     * POST /v1/map/rename
     */
    @ApiOperation("地图重命名")
    @PostMapping("/map/rename")
    public ResponseEntity<Map<String, Object>> renameMap(@RequestBody Map<String, String> params) {
        try {
            String map = params.get("map");
            String rename = params.get("rename");

            if (StringUtils.isBlank(map) || StringUtils.isBlank(rename)) {
                return buildErrorResponse(1, "请求参数不合法：缺少map或rename参数");
            }

            // 先尝试本地重命名
            int result = mapProjectService.renameMap(map, rename);
            if (result > 0) {
                Map<String, Object> response = new HashMap<>();
                response.put("status", true);
                return ResponseEntity.ok(response);
            }

            return buildErrorResponse(2, "地图不存在");

        } catch (Exception e) {
            log.error("地图重命名异常", e);
            return buildErrorResponse(4, "内部错误: " + e.getMessage());
        }
    }

    // ==================== 3.2 瓦片地图接口 ====================

    /**
     * 3.2.1 获取瓦片地图参数
     * GET /v1/tilemap/details?map=地图名
     */
    @ApiOperation("获取瓦片地图参数")
    @GetMapping("/tilemap/details")
    public ResponseEntity<Map<String, Object>> getTilemapDetails(
            @ApiParam("地图名称") @RequestParam("map") String mapName) {
        try {
            if (StringUtils.isBlank(mapName)) {
                return buildErrorResponse(1, "请求参数不合法：缺少map参数");
            }

            // 从config.json获取配置
            JSONObject tilemapConfig = getTilemapConfig(mapName);
            if (tilemapConfig == null) {
                return buildErrorResponse(2, "地图配置不存在");
            }

            Map<String, Object> response = new HashMap<>();
            response.put("status", true);

            // 获取图片尺寸
            int imageWidth = tilemapConfig.getIntValue("ImageWidth", 256);
            int imageHeight = tilemapConfig.getIntValue("ImageHeight", 256);

            // 优先使用config.json中的地图参数
            double originX = tilemapConfig.getDoubleValue("OriginX");
            double originY = tilemapConfig.getDoubleValue("OriginY");
            double mapWidth = tilemapConfig.getDoubleValue("MapWidth");
            double mapHeight = tilemapConfig.getDoubleValue("MapHeight");
            double resolution = tilemapConfig.getDoubleValue("Resolution");

            // 如果config.json中没有这些字段，则使用Layers信息计算
            if (mapWidth == 0 || mapHeight == 0) {
                List<JSONObject> layers = tilemapConfig.getList("Layers", JSONObject.class);
                if (layers == null || layers.isEmpty()) {
                    return buildErrorResponse(2, "地图配置中没有Layers信息");
                }

                // 使用最高层级（最后一个）来计算地图总尺寸
                JSONObject highestLayer = layers.get(layers.size() - 1);
                int highestColCount = highestLayer.getIntValue("ColCount");
                int highestRowCount = highestLayer.getIntValue("RowCount");
                resolution = highestLayer.getDoubleValue("Resolution");

                // 计算地图总尺寸
                // 地图宽度 = 最高层列数 * 图片宽度 * 分辨率
                // 地图高度 = 最高层行数 * 图片高度 * 分辨率
                mapWidth = highestColCount * imageWidth * resolution;
                mapHeight = highestRowCount * imageHeight * resolution;
            }

            Map<String, Object> projection = new HashMap<>();
            projection.put("extent", new BigDecimal[]{
                    BigDecimal.valueOf(originX),
                    BigDecimal.valueOf(originY),
                    BigDecimal.valueOf(originX + mapWidth),
                    BigDecimal.valueOf(originY + mapHeight)
            });

            response.put("projection", projection);
            response.put("layer_cnt", tilemapConfig.getIntValue("LayerCount"));
            response.put("min_zoom", 1);
            response.put("max_zoom", tilemapConfig.getIntValue("LayerCount", 5));
            response.put("image_width", imageWidth);
            response.put("image_height", imageHeight);
            response.put("origin_x", originX);
            response.put("origin_y", originY);
            response.put("resolution", resolution);
            response.put("map_width", mapWidth);
            response.put("map_height", mapHeight);

            return ResponseEntity.ok(response);

        } catch (Exception e) {
            log.error("获取瓦片地图参数异常", e);
            return buildErrorResponse(4, "内部错误: " + e.getMessage());
        }
    }

    /**
     * 3.2.2 获取地图瓦片
     * GET /v1/tilemap/tile/{地图名}/{层级}/{x索引}/{y索引}
     *
     * 说明：
     * - 层级: 起始值为1,代表第一层级 (layerIndex = zoom - 1)
     * - x索引: 列索引(Col)，起始值为0
     * - y索引: 行索引(Row)，起始值为0
     * - Images数组按Col分组，每组内按Row排列
     *   所以 tileIndex = Col * RowCount + Row = x * rowCount + y
     */
    @ApiOperation("获取地图瓦片")
    @GetMapping("/tilemap/tile/{mapName}/{zoom}/{x}/{y}")
    public ResponseEntity<byte[]> getTile(
            @ApiParam("地图名称") @PathVariable("mapName") String mapName,
            @ApiParam("层级") @PathVariable("zoom") int zoom,
            @ApiParam("X索引(列)") @PathVariable("x") int x,
            @ApiParam("Y索引(行)") @PathVariable("y") int y) {

        try {
            if (StringUtils.isBlank(mapName) || zoom < 1 || x < 0 || y < 0) {
                return ResponseEntity.badRequest().build();
            }

            // 获取config.json配置
            JSONObject tilemapConfig = getTilemapConfig(mapName);
            if (tilemapConfig == null) {
                log.warn("获取地图 {} 的config.json失败", mapName);
                return ResponseEntity.notFound().build();
            }

            int layerIndex = zoom - 1;

            // 获取该层级的行列数
            int colCount = getLayerColCount(tilemapConfig, layerIndex);
            int rowCount = getLayerRowCount(tilemapConfig, layerIndex);

            if (colCount <= 0 || rowCount <= 0) {
                log.warn("地图 {} 层级 {} 的行列数无效: colCount={}, rowCount={}", mapName, zoom, colCount, rowCount);
                return ResponseEntity.notFound().build();
            }

            // 验证坐标范围
            if (x < 0 || x >= colCount || y < 0 || y >= rowCount) {
                log.warn("瓦片坐标超出范围: x={}, y={}, colCount={}, rowCount={},layerIndex={}", x, y, colCount, rowCount, layerIndex);
                return ResponseEntity.notFound().build();
            }

            // Images数组按Col分组，每组内按Row排列
            // 所以 tileIndex = Col * RowCount + Row = x * rowCount + y
            int tileIndex = x * rowCount + y;

            // 构建瓦片URL: tilemapBaseUrl/tilemap/{mapName}/{layerIndex}/{tileIndex}.png
            String tileUrl = tilemapBaseUrl + "/tilemap/" + mapName + "/" + layerIndex + "/" + tileIndex + ".png";
            log.info("获取瓦片: {}, tileIndex={}, col={}, row={}, colCount={}, rowCount={}",
                    tileUrl, tileIndex, x, y, colCount, rowCount);

            ResponseEntity<byte[]> tileResponse = restTemplate.getForEntity(tileUrl, byte[].class);
            if (tileResponse.getStatusCode() == HttpStatus.OK && tileResponse.getBody() != null) {
                return ResponseEntity.ok()
                        .contentType(MediaType.IMAGE_PNG)
                        .body(tileResponse.getBody());
            }

            return ResponseEntity.notFound().build();

        } catch (Exception e) {
            log.error("获取地图瓦片异常: map={}, zoom={}, x={}, y={}", mapName, zoom, x, y, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    // ==================== 3.3 路网矢量地图接口 ====================

    /**
     * 3.3.1 获取路网数据
     * GET /v1/roadmap/geojson?map=地图名
     */
    @ApiOperation("获取路网GeoJSON数据")
    @GetMapping("/roadmap/geojson")
    public ResponseEntity<Map<String, Object>> getRoadmapGeoJson(
            @ApiParam("地图名称") @RequestParam("map") String mapName) {
        try {
            if (StringUtils.isBlank(mapName)) {
                return buildErrorResponse(1, "请求参数不合法：缺少map参数");
            }

            // 先从本地数据库获取
            String geoJson = roadmapService.getGeoJsonByMapName(mapName);

            if (StringUtils.isNotBlank(geoJson)) {
                Map<String, Object> response = new HashMap<>();
                response.put("status", true);
                response.put("data", JSON.parseObject(geoJson));
                return ResponseEntity.ok(response);
            }

            return buildErrorResponse(2, "路网数据不存在");

        } catch (Exception e) {
            log.error("获取路网数据异常", e);
            return buildErrorResponse(4, "内部错误: " + e.getMessage());
        }
    }

    /**
     * 3.3.2 保存路网数据
     * POST /v1/roadmap/geojson
     */
    @ApiOperation("保存路网GeoJSON数据")
    @PostMapping("/roadmap/geojson")
    public ResponseEntity<Map<String, Object>> saveRoadmapGeoJson(@RequestBody JSONObject geoJsonData) {
        try {
            String mapName = geoJsonData.getString("map");
            String geoJson = geoJsonData.getString("features");

            if (StringUtils.isBlank(mapName)) {
                return buildErrorResponse(1, "请求参数不合法：缺少map参数");
            }
            Map<String, Object> response = new HashMap<>();
            // 保存到本地数据库
            int result = roadmapService.saveRoadmap(mapName, geoJson);
            if (result < 0){
                response.put("status", false);
                return ResponseEntity.ok(response);
            }

            response.put("status", true);
            return ResponseEntity.ok(response);

        } catch (Exception e) {
            log.error("保存路网数据异常", e);
            return buildErrorResponse(4, "内部错误: " + e.getMessage());
        }
    }

    // ==================== 辅助方法 ====================

    /**
     * 获取地图的config.json配置（带缓存）
     */
    private JSONObject getTilemapConfig(String mapName) {
        if (StringUtils.isBlank(mapName)) {
            return null;
        }

        AtomicReference<JSONObject> cacheRef = configCache.computeIfAbsent(mapName, k -> new AtomicReference<>());
        JSONObject cached = cacheRef.get();
        if (cached != null) {
            return cached;
        }

        try {
            // 从tilemapBaseUrl服务器获取config.json
            String configUrl = tilemapBaseUrl + "/tilemap/" + mapName + "/config.json";
            ResponseEntity<String> response = restTemplate.getForEntity(configUrl, String.class);
            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                JSONObject config = JSON.parseObject(response.getBody());
                cacheRef.set(config);
                return config;
            }
        } catch (Exception e) {
            log.error("获取地图 {} 的config.json失败", mapName, e);
        }

        return null;
    }

    /**
     * 根据层级索引获取该层级的ColCount
     */
    private int getLayerColCount(JSONObject config, int layerIndex) {
        if (config == null) {
            return -1;
        }

        List<JSONObject> layers = config.getList("Layers", JSONObject.class);
        if (layers == null) {
            return -1;
        }

        for (JSONObject layerObj : layers) {
            if (layerObj.getIntValue("Index") == layerIndex) {
                return layerObj.getIntValue("ColCount");
            }
        }
        return -1;
    }

    /**
     * 根据层级索引获取该层级的RowCount
     */
    private int getLayerRowCount(JSONObject config, int layerIndex) {
        if (config == null) {
            return -1;
        }

        List<JSONObject> layers = config.getList("Layers", JSONObject.class);
        if (layers == null) {
            return -1;
        }

        for (JSONObject layerObj : layers) {
            if (layerObj.getIntValue("Index") == layerIndex) {
                return layerObj.getIntValue("RowCount");
            }
        }
        return -1;
    }

    /**
     * 根据层级和坐标计算瓦片索引
     * 索引 = y * ColCount + x
     */
    private int calculateTileIndex(int colCount, int x, int y) {
        return y * colCount + x;
    }

    /**
     * 构建错误响应
     */
    private ResponseEntity<Map<String, Object>> buildErrorResponse(int code, String msg) {
        Map<String, Object> response = new HashMap<>();
        response.put("status", false);
        response.put("code", code);
        response.put("msg", msg);
        return ResponseEntity.ok(response);
    }

    /**
     * 根据文件扩展名获取Content-Type
     */
    private String getContentType(String format) {
        if (format == null) {
            return "image/png";
        }
        switch (format.toLowerCase()) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "webp":
                return "image/webp";
            default:
                return "image/png";
        }
    }
}
