package com.usky.cdi.service.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.usky.cdi.service.config.Gb28181VideoProperties;
import com.usky.cdi.service.vo.video.VideoChannelVO;
import com.usky.cdi.service.vo.video.VideoDeviceItemVO;
import com.usky.cdi.service.vo.video.VideoPlayInfoVO;
import com.usky.common.core.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * GB28181 流媒体平台（WVP 等）HTTP API 客户端
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class Gb28181VideoPlatformClient {

    private static final int CONNECT_TIMEOUT_MS = 8000;
    private static final int SOCKET_TIMEOUT_MS = 15000;

    private final Gb28181VideoProperties properties;

    private volatile String accessToken;
    private volatile long tokenExpireAt;

    /**
     * 检查配置：SIP 端口可达 + API 登录成功
     */
    public JSONObject checkConfiguration() {
        JSONObject result = new JSONObject();
        result.put("sipId", properties.getSipId());
        result.put("sipDomain", properties.getSipDomain());
        result.put("sipIp", properties.getSipIp());
        result.put("sipPort", properties.getSipPort());
        result.put("cascadePort", properties.getCascadePort());
        result.put("rtpPortMin", properties.getRtpPortMin());
        result.put("rtpPortMax", properties.getRtpPortMax());

        boolean sipReachable = testTcpPort(properties.getSipIp(), properties.getSipPort());
        result.put("sipPortReachable", sipReachable);

        boolean cascadeReachable = testTcpPort(properties.getSipIp(), properties.getCascadePort());
        result.put("cascadePortReachable", cascadeReachable);

        try {
            login(true);
            result.put("apiLoginSuccess", true);
            result.put("apiBaseUrl", properties.getApiBaseUrl());
        } catch (Exception e) {
            result.put("apiLoginSuccess", false);
            result.put("apiLoginMessage", e.getMessage());
        }
        result.put("success", sipReachable && Boolean.TRUE.equals(result.getBoolean("apiLoginSuccess")));
        return result;
    }

    /**
     * 查询平台全部设备及通道
     */
    public List<VideoDeviceItemVO> queryAllDevicesWithChannels() {
        login(false);
        List<VideoDeviceItemVO> devices = new ArrayList<>();
        int page = 1;
        int pageSize = 100;
        int total = Integer.MAX_VALUE;

        while ((page - 1) * pageSize < total) {
            JSONObject pageData = getJson("/api/device/query/devices", buildPageParam(page, pageSize));
            JSONArray list = pageData.getJSONArray("list");
            if (list == null || list.isEmpty()) {
                break;
            }
            total = pageData.getIntValue("total");
            for (int i = 0; i < list.size(); i++) {
                JSONObject deviceJson = list.getJSONObject(i);
                VideoDeviceItemVO device = parseDevice(deviceJson);
                device.setChannels(queryChannels(device.getDeviceId()));
                devices.add(device);
            }
            page++;
        }
        return devices;
    }

    /**
     * 发起点播：WVP 向设备发 SIP INVITE，RTP 流汇入 114.80.201.142 媒体服务器
     */
    public VideoPlayInfoVO startPlay(String deviceId, String channelId) {
        login(false);
        JSONObject data = getJson("/api/play/start/" + deviceId + "/" + channelId, null);
        return parsePlayInfo(deviceId, channelId, data);
    }

    /**
     * 停止点播
     */
    public void stopPlay(String deviceId, String channelId) {
        login(false);
        getJson("/api/play/stop/" + deviceId + "/" + channelId, null);
        log.info("已停止点播 deviceId={} channelId={}", deviceId, channelId);
    }

    /**
     * 获取通道实时预览地址（内部调用 startPlay）
     */
    public String queryPlayUrl(String deviceId, String channelId) {
        VideoPlayInfoVO playInfo = startPlay(deviceId, channelId);
        return playInfo.getPreferredPlayUrl(properties.getPreferredProtocol());
    }

    private VideoPlayInfoVO parsePlayInfo(String deviceId, String channelId, JSONObject data) {
        VideoPlayInfoVO info = new VideoPlayInfoVO();
        info.setDeviceId(deviceId);
        info.setChannelId(channelId);
        info.setApp(data.getString("app"));
        info.setStream(data.getString("stream"));
        info.setFlvUrl(firstNonBlank(data, "flv", "ws_flv"));
        info.setHlsUrl(data.getString("hls"));
        info.setRtspUrl(data.getString("rtsp"));
        info.setRtcUrl(data.getString("rtc"));
        info.setWsFlvUrl(data.getString("ws_flv"));
        info.setStreamStatus(info.getPreferredPlayUrl(properties.getPreferredProtocol()) != null ? "PUSHING" : "FAILED");
        return info;
    }

    private String firstNonBlank(JSONObject data, String... keys) {
        for (String key : keys) {
            String value = data.getString(key);
            if (value != null && !value.trim().isEmpty()) {
                return value;
            }
        }
        return null;
    }

    public List<VideoChannelVO> queryChannels(String deviceId) {
        login(false);
        JSONObject data = getJson("/api/device/query/devices/" + deviceId + "/channels", buildPageParam(1, 500));
        JSONArray list = data.getJSONArray("list");
        List<VideoChannelVO> channels = new ArrayList<>();
        if (list == null) {
            return channels;
        }
        for (int i = 0; i < list.size(); i++) {
            channels.add(parseChannel(list.getJSONObject(i)));
        }
        return channels;
    }

    private VideoDeviceItemVO parseDevice(JSONObject json) {
        VideoDeviceItemVO device = new VideoDeviceItemVO();
        device.setDeviceId(json.getString("deviceId"));
        device.setDeviceName(json.getString("name"));
        device.setManufacturer(json.getString("manufacturer"));
        Boolean onLine = json.getBoolean("onLine");
        if (onLine == null) {
            onLine = json.getBoolean("online");
        }
        device.setStatus(Boolean.TRUE.equals(onLine) ? "ON" : "OFF");
        return device;
    }

    private VideoChannelVO parseChannel(JSONObject json) {
        VideoChannelVO channel = new VideoChannelVO();
        channel.setChannelId(json.getString("channelId"));
        channel.setChannelName(json.getString("name"));
        Boolean onLine = json.getBoolean("onLine");
        if (onLine == null) {
            onLine = json.getBoolean("online");
        }
        channel.setStatus(Boolean.TRUE.equals(onLine) ? "ON" : "OFF");
        return channel;
    }

    private Map<String, String> buildPageParam(int page, int count) {
        Map<String, String> param = new HashMap<>(4);
        param.put("page", String.valueOf(page));
        param.put("count", String.valueOf(count));
        return param;
    }

    private synchronized void login(boolean force) {
        long now = System.currentTimeMillis();
        if (!force && accessToken != null && now < tokenExpireAt) {
            return;
        }
        JSONObject body = new JSONObject();
        body.put("username", properties.getApiUsername());
        body.put("password", properties.getApiPassword());

        String response = doPostJson(properties.getApiBaseUrl() + "/api/user/login", body.toJSONString(), null);
        JSONObject root = parseResponse(response);
        JSONObject data = root.getJSONObject("data");
        if (data == null || data.getString("accessToken") == null) {
            throw new BusinessException("国标视频平台登录失败，未返回 accessToken");
        }
        accessToken = data.getString("accessToken");
        tokenExpireAt = now + 50 * 60 * 1000L;
        log.info("国标视频平台 API 登录成功");
    }

    private JSONObject getJson(String path, Map<String, String> queryParam) {
        String response = doGet(properties.getApiBaseUrl() + path, queryParam, accessToken);
        JSONObject root = parseResponse(response);
        return root.getJSONObject("data") != null ? root.getJSONObject("data") : new JSONObject();
    }

    private JSONObject parseResponse(String response) {
        if (response == null || response.trim().isEmpty()) {
            throw new BusinessException("国标视频平台 API 无响应");
        }
        JSONObject root = JSON.parseObject(response);
        Integer code = root.getInteger("code");
        if (code != null && code != 0) {
            throw new BusinessException("国标视频平台 API 错误：" + root.getString("msg"));
        }
        return root;
    }

    private boolean testTcpPort(String host, int port) {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(host, port), CONNECT_TIMEOUT_MS);
            return true;
        } catch (Exception e) {
            log.warn("端口检测失败 {}:{} - {}", host, port, e.getMessage());
            return false;
        }
    }

    private String doGet(String url, Map<String, String> param, String token) {
        CloseableHttpResponse response = null;
        try (CloseableHttpClient client = createClient()) {
            URIBuilder builder = new URIBuilder(url);
            if (param != null) {
                for (Map.Entry<String, String> entry : param.entrySet()) {
                    builder.addParameter(entry.getKey(), entry.getValue());
                }
            }
            URI uri = builder.build();
            HttpGet httpGet = new HttpGet(uri);
            if (token != null) {
                httpGet.setHeader("access-token", token);
            }
            response = client.execute(httpGet);
            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw new BusinessException("国标视频平台 GET 请求失败：" + e.getMessage());
        } finally {
            closeQuietly(response);
        }
    }

    private String doPostJson(String url, String json, String token) {
        CloseableHttpResponse response = null;
        try (CloseableHttpClient client = createClient()) {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("Content-Type", "application/json");
            if (token != null) {
                httpPost.setHeader("access-token", token);
            }
            httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
            response = client.execute(httpPost);
            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw new BusinessException("国标视频平台 POST 请求失败：" + e.getMessage());
        } finally {
            closeQuietly(response);
        }
    }

    private CloseableHttpClient createClient() {
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(CONNECT_TIMEOUT_MS)
                .setSocketTimeout(SOCKET_TIMEOUT_MS)
                .setConnectionRequestTimeout(CONNECT_TIMEOUT_MS)
                .build();
        return HttpClients.custom().setDefaultRequestConfig(config).build();
    }

    private void closeQuietly(CloseableHttpResponse response) {
        if (response != null) {
            try {
                response.close();
            } catch (Exception ignored) {
                // ignore
            }
        }
    }
}
