package com.aegis.camera.service.impl;

import com.aegis.camera.config.ZLMediaKitProperties;
import com.aegis.camera.domain.Camera;
import com.aegis.camera.service.StreamProtocolHandler;
import com.aegis.common.exception.ServiceException;
import com.aegis.common.utils.StringUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

/**
 * RTSP 协议处理器
 *
 * 通过 ZLMediaKit 的 addStreamProxy / close_stream 接口实现 RTSP 拉流。
 */
@Slf4j
@Component
public class RtspStreamProtocolHandler implements StreamProtocolHandler {

    private final RestTemplate restTemplate = new RestTemplate();
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String getProtocol() {
        return "rtsp";
    }

    @Override
    public boolean startStream(Camera camera, ZLMediaKitProperties properties) {
        if (camera == null) {
            throw new ServiceException("摄像头信息不能为空");
        }
        String url = extractRtspUrl(camera);
        if (StringUtils.isBlank(url)) {
            throw new ServiceException("摄像头扩展配置中 RTSP 地址不能为空");
        }

        String streamId = camera.getCameraCode();
        if (StringUtils.isBlank(streamId)) {
            throw new ServiceException("摄像头编码不能为空，无法作为流标识");
        }

        URI uri = buildApiUri(properties, "/index/api/addStreamProxy", new HashMap<String, Object>() {{
            put("vhost", properties.getVhost());
            put("app", properties.getApp());
            put("stream", streamId);
            put("url", url);
            put("enable_hls", properties.isEnableHls() ? 1 : 0);
            put("enable_mp4", properties.isEnableMp4() ? 1 : 0);
            put("rtp_type", 0);
            put("secret", properties.getSecret());
        }});

        try {
            log.info("RTSP startStream 请求：{}", uri);
            ResponseEntity<Map> response = restTemplate.exchange(
                new RequestEntity<>(HttpMethod.GET, uri), Map.class);
            Map<String, Object> body = response.getBody();
            log.info("RTSP startStream 响应：{}", body);
            return isOk(body);
        } catch (Exception e) {
            log.error("调用 ZLMediaKit 启动 RTSP 流失败, cameraCode={}, url={}", streamId, url, e);
            throw new ServiceException("调用 ZLMediaKit 启动 RTSP 流失败：" + e.getMessage());
        }
    }

    @Override
    public boolean stopStream(Camera camera, ZLMediaKitProperties properties) {
        if (camera == null) {
            throw new ServiceException("摄像头信息不能为空");
        }
        String streamId = camera.getCameraCode();
        if (StringUtils.isBlank(streamId)) {
            throw new ServiceException("摄像头编码不能为空，无法作为流标识");
        }

        URI uri = buildApiUri(properties, "/index/api/close_stream", new HashMap<String, Object>() {{
            put("vhost", properties.getVhost());
            put("app", properties.getApp());
            put("stream", streamId);
            put("force", 1);
            put("secret", properties.getSecret());
        }});

        try {
            log.info("RTSP stopStream 请求：{}", uri);
            ResponseEntity<Map> response = restTemplate.exchange(
                new RequestEntity<>(HttpMethod.GET, uri), Map.class);
            Map<String, Object> body = response.getBody();
            log.info("RTSP stopStream 响应：{}", body);
            return isOk(body);
        } catch (Exception e) {
            log.error("调用 ZLMediaKit 停止 RTSP 流失败, cameraCode={}", streamId, e);
            throw new ServiceException("调用 ZLMediaKit 停止 RTSP 流失败：" + e.getMessage());
        }
    }

    /**
     * 从 Camera.extraConfig 中解析 RTSP 地址。
     * 约定 extraConfig 为 JSON，对象中包含 "url" 字段：
     * { "url": "rtsp://user:pass@ip:554/stream", ... }
     */
    private String extractRtspUrl(Camera camera) {
        String extra = camera.getExtraConfig();
        if (StringUtils.isBlank(extra)) {
            return null;
        }
        try {
            Map<String, Object> map = objectMapper.readValue(
                extra, new TypeReference<Map<String, Object>>() {});
            Object url = map.get("url");
            return url != null ? String.valueOf(url) : null;
        } catch (Exception e) {
            log.warn("解析摄像头扩展配置失败，无法获取 RTSP 地址, cameraId={}, extraConfig={}",
                camera.getId(), extra, e);
            return null;
        }
    }

    private URI buildApiUri(ZLMediaKitProperties properties, String path, Map<String, Object> params) {
        String base = properties.getBaseUrl();
        if (StringUtils.isBlank(base)) {
            throw new ServiceException("ZLMediaKit base-url 未配置");
        }
        String normalizedBase = trimTrailingSlash(base);
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(
            normalizedBase + path);
        if (params != null) {
            params.forEach(builder::queryParam);
        }
        try {
            return builder.build(true).toUri();
        } catch (IllegalArgumentException e) {
            throw new ServiceException("构建 ZLMediaKit 接口地址失败：" + e.getMessage());
        }
    }

    private String trimTrailingSlash(String url) {
        if (url == null) {
            return null;
        }
        while (url.endsWith("/")) {
            url = url.substring(0, url.length() - 1);
        }
        return url;
    }

    /**
     * 判断 ZLMediaKit 返回是否成功。
     * 通常 ZLM HTTP API 返回形如：{"code":0, "msg":"success", ...}
     */
    @SuppressWarnings("unchecked")
    private boolean isOk(Map body) {
        if (body == null) {
            return false;
        }
        Object code = body.get("code");
        if (code == null) {
            return false;
        }
        try {
            return Integer.parseInt(code.toString()) == 0;
        } catch (NumberFormatException e) {
            return false;
        }
    }
}