package com.genersoft.iot.vmp.service.redisMsg.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig;
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.media.event.hook.Hook;
import com.genersoft.iot.vmp.media.event.hook.HookSubscribe;
import com.genersoft.iot.vmp.media.event.hook.HookType;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.ISendRtpServerService;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class RedisRpcServiceImpl implements IRedisRpcService {


    @Autowired
    private RedisRpcConfig redisRpcConfig;

    @Autowired
    private UserSetting userSetting;

    @Autowired
    private HookSubscribe hookSubscribe;

    @Autowired
    private SSRCFactory ssrcFactory;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private IMediaServerService mediaServerService;

    @Autowired
    private ISendRtpServerService sendRtpServerService;

    private RedisRpcRequest buildRequest(String uri, Object param) {
        RedisRpcRequest request = new RedisRpcRequest();
        request.setFromId(userSetting.getServerId());
        request.setParam(param);
        request.setUri(uri);
        return request;
    }

    @Override
    public SendRtpInfo getSendRtpItem(String callId) {
        RedisRpcRequest request = buildRequest("sendRtp/getSendRtpItem", callId);
        RedisRpcResponse response = redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
        if (response.getBody() == null) {
            return null;
        }
        return (SendRtpInfo)redisTemplate.opsForValue().get(response.getBody().toString());
    }

    @Override
    public WVPResult<?> startSendRtp(String callId, SendRtpInfo sendRtpItem) {
        log.info("[请求其他WVP] 开始推流，wvp：{}， {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream());
        RedisRpcRequest request = buildRequest("sendRtp/startSendRtp", callId);
        request.setToId(sendRtpItem.getServerId());
        RedisRpcResponse response = redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public WVPResult stopSendRtp(String callId) {
        SendRtpInfo sendRtpItem = (SendRtpInfo)redisTemplate.opsForValue().get(callId);
        if (sendRtpItem == null) {
            log.info("[请求其他WVP] 停止推流, 未找到redis中的发流信息， key：{}", callId);
            return WVPResult.fail(ErrorCode.ERROR100.getCode(), "未找到发流信息");
        }
        log.info("[请求其他WVP] 停止推流，wvp：{}， {}/{}", sendRtpItem.getServerId(), sendRtpItem.getApp(), sendRtpItem.getStream());
        RedisRpcRequest request = buildRequest("sendRtp/stopSendRtp", callId);
        request.setToId(sendRtpItem.getServerId());
        RedisRpcResponse response = redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public long waitePushStreamOnline(SendRtpInfo sendRtpItem, CommonCallback<Integer> callback) {
        log.info("[请求所有WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
        // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者
        Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null);
        RedisRpcRequest request = buildRequest("streamPush/waitePushStreamOnline", sendRtpItem);
        request.setToId(sendRtpItem.getServerId());
        hookSubscribe.addSubscribe(hook, (hookData) -> {

            // 读取redis中的上级点播信息，生成sendRtpItm发送出去
            if (sendRtpItem.getSsrc() == null) {
                // 上级平台点播时不使用上级平台指定的ssrc，使用自定义的ssrc，参考国标文档-点播外域设备媒体流SSRC处理方式
                String ssrc = "Play".equalsIgnoreCase(sendRtpItem.getSessionName()) ? ssrcFactory.getPlaySsrc(hookData.getMediaServer().getId()) : ssrcFactory.getPlayBackSsrc(hookData.getMediaServer().getId());
                sendRtpItem.setSsrc(ssrc);
            }
            sendRtpItem.setMediaServerId(hookData.getMediaServer().getId());
            sendRtpItem.setLocalIp(hookData.getMediaServer().getSdpIp());
            sendRtpItem.setServerId(userSetting.getServerId());
            sendRtpServerService.update(sendRtpItem);
            if (callback != null) {
                callback.run(sendRtpItem.getChannelId());
            }
            hookSubscribe.removeSubscribe(hook);
            redisRpcConfig.removeCallback(request.getSn());
        });

        redisRpcConfig.request(request, response -> {
            if (response.getBody() == null) {
                log.info("[请求所有WVP监听流上线] 流上线,但是未找到发流信息：{}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
                return;
            }
            log.info("[请求所有WVP监听流上线] 流上线 {}/{}->{}", sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.toString());

            if (callback != null) {
                callback.run(Integer.parseInt(response.getBody().toString()));
            }
            hookSubscribe.removeSubscribe(hook);
        });
        return request.getSn();
    }

    @Override
    public void stopWaitePushStreamOnline(SendRtpInfo sendRtpItem) {
        log.info("[停止WVP监听流上线] {}/{}", sendRtpItem.getApp(), sendRtpItem.getStream());
        Hook hook = Hook.getInstance(HookType.on_media_arrival, sendRtpItem.getApp(), sendRtpItem.getStream(), null);
        hookSubscribe.removeSubscribe(hook);
        RedisRpcRequest request = buildRequest("streamPush/stopWaitePushStreamOnline", sendRtpItem);
        request.setToId(sendRtpItem.getServerId());
        redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
    }

    @Override
    public void rtpSendStopped(String callId) {
        SendRtpInfo sendRtpItem = (SendRtpInfo)redisTemplate.opsForValue().get(callId);
        if (sendRtpItem == null) {
            log.info("[停止WVP监听流上线] 未找到redis中的发流信息， key：{}", callId);
            return;
        }
        RedisRpcRequest request = buildRequest("streamPush/rtpSendStopped", callId);
        request.setToId(sendRtpItem.getServerId());
        redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
    }

    @Override
    public void removeCallback(long key) {
        redisRpcConfig.removeCallback(key);
    }

    @Override
    public long onStreamOnlineEvent(String app, String stream, CommonCallback<StreamInfo> callback) {

        log.info("[请求所有WVP监听流上线] {}/{}", app, stream);
        // 监听流上线。 流上线直接发送sendRtpItem消息给实际的信令处理者
        Hook hook = Hook.getInstance(HookType.on_media_arrival, app, stream);
        StreamInfo streamInfoParam = new StreamInfo();
        streamInfoParam.setApp(app);
        streamInfoParam.setStream(stream);
        RedisRpcRequest request = buildRequest("streamPush/onStreamOnlineEvent", streamInfoParam);
        hookSubscribe.addSubscribe(hook, (hookData) -> {
            log.info("[请求所有WVP监听流上线] 监听流上线 {}/{}", app, stream);
            if (callback != null) {
                callback.run(mediaServerService.getStreamInfoByAppAndStream(hookData.getMediaServer(),
                        app, stream, hookData.getMediaInfo(),
                        hookData.getMediaInfo() != null ? hookData.getMediaInfo().getCallId() : null));
            }
            hookSubscribe.removeSubscribe(hook);
            redisRpcConfig.removeCallback(request.getSn());
        });

        redisRpcConfig.request(request, response -> {
            if (response.getBody() == null) {
                log.info("[请求所有WVP监听流上线] 流上线,但是未找到发流信息：{}/{}", app, stream);
                return;
            }
            log.info("[请求所有WVP监听流上线] 流上线 {}/{}", app, stream);

            if (callback != null) {
                callback.run(JSON.parseObject(response.getBody().toString(), StreamInfo.class));
            }
            hookSubscribe.removeSubscribe(hook);
        });
        return request.getSn();
    }

    @Override
    public void unPushStreamOnlineEvent(String app, String stream) {
        StreamInfo streamInfoParam = new StreamInfo();
        streamInfoParam.setApp(app);
        streamInfoParam.setStream(stream);
        RedisRpcRequest request = buildRequest("streamPush/unPushStreamOnlineEvent", streamInfoParam);
        redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
    }

    @Override
    public void subscribeCatalog(int id, int cycle) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("id", id);
        jsonObject.put("cycle", cycle);
        RedisRpcRequest request = buildRequest("device/subscribeCatalog", jsonObject);
        redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
    }

    @Override
    public void subscribeMobilePosition(int id, int cycle, int interval) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("id", id);
        jsonObject.put("cycle", cycle);
        jsonObject.put("interval", cycle);
        RedisRpcRequest request = buildRequest("device/subscribeMobilePosition", jsonObject);
        redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
    }

    @Override
    public boolean updatePlatform(String serverId, Platform platform) {
        RedisRpcRequest request = buildRequest("platform/update", platform);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 40, TimeUnit.MILLISECONDS);
        if(response == null) {
            return false;
        }
        return Boolean.parseBoolean(response.getBody().toString());
    }

    @Override
    public void catalogEventPublish(String serverId, CatalogEvent event) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("platform", event.getPlatform());
        jsonObject.put("channels", event.getChannels());
        jsonObject.put("type", event.getType());
        RedisRpcRequest request = buildRequest("platform/catalogEventPublish", jsonObject);
        if (serverId != null) {
            request.setToId(serverId);
        }
        redisRpcConfig.request(request, 10, TimeUnit.MILLISECONDS);
    }

    @Override
    public WVPResult<SyncStatus> devicesSync(String serverId, String deviceId) {
        RedisRpcRequest request = buildRequest("device/devicesSync", deviceId);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 100, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public SyncStatus getChannelSyncStatus(String serverId, String deviceId) {
        RedisRpcRequest request = buildRequest("device/getChannelSyncStatus", deviceId);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 100, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), SyncStatus.class);
    }

    @Override
    public WVPResult<String> deviceBasicConfig(String serverId, Device device, BasicParam basicParam) {
        RedisRpcRequest request = buildRequest("device/deviceBasicConfig", JSONObject.toJSONString(basicParam));
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public WVPResult<String> deviceConfigQuery(String serverId, Device device, String channelId, String configType) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        jsonObject.put("configType", configType);
        RedisRpcRequest request = buildRequest("device/deviceConfigQuery", jsonObject);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public void teleboot(String serverId, Device device) {
        RedisRpcRequest request = buildRequest("device/teleboot", device.getDeviceId());
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        if (response.getStatusCode() != ErrorCode.SUCCESS.getCode()) {
            throw new ControllerException(response.getStatusCode(), response.getBody().toString());
        }
    }

    @Override
    public WVPResult<String> recordControl(String serverId, Device device, String channelId, String recordCmdStr) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        jsonObject.put("recordCmdStr", recordCmdStr);
        RedisRpcRequest request = buildRequest("device/record", jsonObject);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public WVPResult<String> guard(String serverId, Device device, String guardCmdStr) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("guardCmdStr", guardCmdStr);
        RedisRpcRequest request = buildRequest("device/guard", jsonObject);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public WVPResult<String> resetAlarm(String serverId, Device device, String channelId, String alarmMethod, String alarmType) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        jsonObject.put("alarmMethod", alarmMethod);
        jsonObject.put("alarmType", alarmType);
        RedisRpcRequest request = buildRequest("device/resetAlarm", jsonObject);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public void iFrame(String serverId, Device device, String channelId) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        RedisRpcRequest request = buildRequest("device/iFrame", jsonObject);
        request.setToId(serverId);
        redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
    }

    @Override
    public WVPResult<String> homePosition(String serverId, Device device, String channelId, Boolean enabled, Integer resetTime, Integer presetIndex) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        jsonObject.put("enabled", enabled);
        jsonObject.put("resetTime", resetTime);
        jsonObject.put("presetIndex", presetIndex);
        RedisRpcRequest request = buildRequest("device/homePosition", jsonObject);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public void dragZoomIn(String serverId, Device device, String channelId, int length, int width, int midpointx,
                           int midpointy, int lengthx, int lengthy) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        jsonObject.put("length", length);
        jsonObject.put("width", width);
        jsonObject.put("midpointx", midpointx);
        jsonObject.put("midpointy", midpointy);
        jsonObject.put("lengthx", lengthx);
        jsonObject.put("lengthy", lengthy);
        RedisRpcRequest request = buildRequest("device/dragZoomIn", jsonObject);
        request.setToId(serverId);
        redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
    }

    @Override
    public void dragZoomOut(String serverId, Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        jsonObject.put("length", length);
        jsonObject.put("width", width);
        jsonObject.put("midpointx", midpointx);
        jsonObject.put("midpointy", midpointy);
        jsonObject.put("lengthx", lengthx);
        jsonObject.put("lengthy", lengthy);
        RedisRpcRequest request = buildRequest("device/dragZoomOut", jsonObject);
        request.setToId(serverId);
        redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
    }

    @Override
    public WVPResult<String> deviceStatus(String serverId, Device device) {
        RedisRpcRequest request = buildRequest("device/deviceStatus", device.getDeviceId());
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public WVPResult<Object> deviceInfo(String serverId, Device device) {
        RedisRpcRequest request = buildRequest("device/info", device.getDeviceId());
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public WVPResult<List<Preset>> queryPreset(String serverId, Device device, String channelId) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
        jsonObject.put("channelId", channelId);
        RedisRpcRequest request = buildRequest("device/queryPreset", jsonObject);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 60000, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }

    @Override
    public WVPResult<String> alarm(String serverId, Device device, String startPriority, String endPriority,
                                   String alarmMethod, String alarmType, String startTime, String endTime) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("device", device.getDeviceId());
//        jsonObject.put("channelId", channelId);
        jsonObject.put("startPriority", startPriority);
        jsonObject.put("endPriority", endPriority);
        jsonObject.put("alarmMethod", alarmMethod);
        jsonObject.put("alarmType", alarmType);
        jsonObject.put("startTime", startTime);
        jsonObject.put("endTime", endTime);
        RedisRpcRequest request = buildRequest("device/alarm", jsonObject);
        request.setToId(serverId);
        RedisRpcResponse response = redisRpcConfig.request(request, 50, TimeUnit.MILLISECONDS);
        return JSON.parseObject(response.getBody().toString(), WVPResult.class);
    }
}
