package cn.iocoder.yudao.module.pms.service.yanfan.sip;

import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.enums.SessionType;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.server.VideoSessionManager;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.device.vo.YfSipDeviceRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.vo.InviteInfo;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.vo.VideoSessionInfo;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.utils.SipUtil;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.utils.ZlmApiUtils;
import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.media.YfMediaServerDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.sip.config.IotSipConfigDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.sip.device.YfSipDeviceDO;
import cn.iocoder.yudao.module.pms.service.yanfan.media.YfMediaServerService;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.config.IotSipConfigService;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.device.YfSipDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.sip.*;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.List;


@Slf4j
@Component
public class SipCmdImpl implements ISipCmd {

    @Autowired
    private VideoSessionManager streamSession;

    @Autowired
    private ReqMsgHeaderBuilder headerBuilder;

    @Autowired
    private IotSipConfigService sipConfigService;

    @Autowired
    private YfMediaServerService mediaServerService;

    @Autowired
    private YfSipDeviceService sipDeviceService;

    @Autowired
    private ZlmApiUtils zlmApiUtils;

    @Autowired
    private VideoSessionManager videoSessionManager;

    @Autowired
    private IInviteService inviteService;

    @Autowired
    @Qualifier(value = "udpSipServer")
    private SipProvider sipserver;

    @Override
    public VideoSessionInfo playStreamCmd(YfSipDeviceRespVO device, String channelId, boolean record) {
        try {
            IotSipConfigDO sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
            if (sipConfig == null) {
                log.error("playStreamCmd sipConfig is null");
                return null;
            }
            YfMediaServerDO mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
            if (mediaInfo == null) {
                log.error("playStreamCmd mediaInfo is null");
                return null;
            }
            VideoSessionInfo info = new VideoSessionInfo();
            info.setMediaServerId(mediaInfo.getServerId());
            info.setDeviceId(device.getDeviceSipId());
            info.setChannelId(channelId);
            info.setStreamMode(device.getStreamMode().toUpperCase());

            String fromTag;
            if (record) {
                info.setType(SessionType.playrecord);
                fromTag = "playrecord";
            } else {
                info.setType(SessionType.play);
                fromTag = "play";
            }
            //创建rtp服务器
            info = mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
            //创建Invite会话
            String content = buildRequestContent(sipConfig, mediaInfo, info);
            Request request = headerBuilder.createInviteRequest(device, sipConfig, channelId, content, info.getSsrc(), fromTag);
            //发送消息
            ClientTransaction transaction = transmitRequest(request);
            log.info("playStreamCmd streamSession: {}", info);
            InviteInfo invite = new  InviteInfo();
                    invite.setSsrc(info.getSsrc());
                    invite.setFromTag(fromTag);
                    invite.setCallId(transaction.getDialog().getCallId().getCallId());
                    invite.setPort(info.getPort());
            log.warn("playStreamCmd invite: {}", invite);
            inviteService.updateInviteInfo(info, invite);
            streamSession.put(info, transaction);
            return info;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public VideoSessionInfo playbackStreamCmd(YfSipDeviceRespVO device, String channelId, String startTime, String endTime) {
        try {
            IotSipConfigDO sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
            if (sipConfig == null) {
                log.error("playbackStreamCmd sipConfig is null");
                return null;
            }
            YfMediaServerDO mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
            if (mediaInfo == null) {
                log.error("playbackStreamCmd mediaInfo is null");
                return null;
            }
            VideoSessionInfo info = new VideoSessionInfo();

                    info.setMediaServerId(mediaInfo.getServerId());
                    info.setDeviceId(device.getDeviceSipId());
                    info.setChannelId(channelId);
                    info.setStreamMode(device.getStreamMode().toUpperCase());
                    info.setType(SessionType.playback);
                    info.setStartTime(startTime);
                    info.setEndTime(endTime);
            //创建rtp服务器
            info = mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
            //创建Invite会话
            String fromTag = "playback" + SipUtil.getNewFromTag();
            String viaTag = SipUtil.getNewViaTag();
            String content = buildRequestContent(sipConfig, mediaInfo, info);
            Request request = headerBuilder.createPlaybackInviteRequest(device, sipConfig, channelId, content, viaTag, fromTag);
            //发送消息
            ClientTransaction transaction = transmitRequest(request);
            log.info("playbackStreamCmd streamSession: {}", info);
//            InviteInfo invite = InviteInfo.builder()
//                    .ssrc(info.getSsrc())
//                    .fromTag(fromTag)
//                    .viaTag(viaTag)
//                    .callId(transaction.getDialog().getCallId().getCallId())
//                    .port(info.getPort()).build();
            InviteInfo invite = new  InviteInfo();
            invite.setSsrc(info.getSsrc());
            invite.setFromTag(fromTag);
            invite.setViaTag(viaTag);
            invite.setCallId(transaction.getDialog().getCallId().getCallId());
            invite.setPort(info.getPort());
            log.warn("playbackStreamCmd invite: {}", invite);
            inviteService.updateInviteInfo(info, invite);
            streamSession.put(info, transaction);
            return info;
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public VideoSessionInfo downloadStreamCmd(YfSipDeviceRespVO device, String channelId,
                                              String startTime, String endTime, int downloadSpeed) {
        try {
            IotSipConfigDO sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
            if (sipConfig == null) {
                log.error("downloadStreamCmd sipConfig is null");
                return null;
            }
            YfMediaServerDO mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
            if (mediaInfo == null) {
                log.error("downloadStreamCmd mediaInfo is null");
                return null;
            }
//            VideoSessionInfo info = VideoSessionInfo.builder()
//                    .mediaServerId(mediaInfo.getServerId())
//                    .deviceId(device.getDeviceSipId())
//                    .channelId(channelId)
//                    .streamMode(device.getStreamMode().toUpperCase())
//                    .type(SessionType.download)
//                    .startTime(startTime)
//                    .endTime(endTime)
//                    .downloadSpeed(downloadSpeed)
//                    .build();
//            ;

            VideoSessionInfo info = new VideoSessionInfo();

            info.setMediaServerId(mediaInfo.getServerId());
            info.setDeviceId(device.getDeviceSipId());
            info.setChannelId(channelId);
            info.setStreamMode(device.getStreamMode().toUpperCase());
            info.setType(SessionType.playback);
            info.setStartTime(startTime);
            info.setEndTime(endTime);
            info.setDownloadSpeed(downloadSpeed);
            //创建rtp服务器
            info = mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
            //创建Invite会话
            String fromTag = "download" + SipUtil.getNewFromTag();;
            String viaTag = SipUtil.getNewViaTag();
            String content = buildRequestContent(sipConfig, mediaInfo, info);
            Request request = headerBuilder.createPlaybackInviteRequest(device, sipConfig, channelId, content, viaTag, fromTag);
            //发送消息
            ClientTransaction transaction = transmitRequest(request);
            log.info("downloadStreamCmd streamSession: {}", info);
//            InviteInfo invite = InviteInfo.builder()
//                    .ssrc(info.getSsrc())
//                    .fromTag(fromTag)
//                    .viaTag(viaTag)
//                    .callId(transaction.getDialog().getCallId().getCallId())
//                    .port(info.getPort()).build();
            InviteInfo invite = new  InviteInfo();
            invite.setSsrc(info.getSsrc());
            invite.setFromTag(fromTag);
            invite.setViaTag(viaTag);
            invite.setCallId(transaction.getDialog().getCallId().getCallId());
            invite.setPort(info.getPort());
            log.warn("downloadStreamCmd invite: {}", invite);
            inviteService.updateInviteInfo(info, invite);
            streamSession.put(info, transaction);
            return info;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void streamByeCmd(YfSipDeviceRespVO device, String channelId, String stream, String ssrc) {
        IotSipConfigDO sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
        if (sipConfig == null) {
            log.error("[发送BYE] sipConfig is null");
            return;
        }
        YfMediaServerDO mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
        if (mediaInfo == null) {
            log.error("[发送BYE] mediaInfo is null");
            return;
        }
        List<VideoSessionInfo> SessionInfoList = streamSession.getSessionInfoForAll(device.getDeviceSipId(), channelId, stream, ssrc);
        if (SessionInfoList == null || SessionInfoList.isEmpty()) {
            log.warn("[发送BYE] 未找到事务信息,设备： device: {}, channel: {}", device.getDeviceSipId(), channelId);
            return;
        }
        for (VideoSessionInfo info : SessionInfoList) {
            try {
                log.warn("[发送BYE] 设备： device: {}, channel: {}, stream: {}, ssrc: {}", device.getDeviceSipId(),
                        info.getChannelId(), info.getStream(), info.getSsrc());
                List<InviteInfo> list = inviteService.getInviteInfoAll(info.getType(), info.getDeviceId(), info.getChannelId(), info.getStream());
                if (list.isEmpty()) {
                    log.warn("[发送BYE] 未找到invite信息,设备： Stream: {}", info.getStream());
                } else {
                    for (InviteInfo invite : list) {
                        // 发送bye消息
                        Request request = headerBuilder.createByeRequest(device, sipConfig, channelId, invite);
                        //获取缓存会话
                        ClientTransaction transaction = videoSessionManager.getclientTransaction(info);
                        if (transaction == null) {
                            log.warn("[发送BYE] transaction is null");
                            continue;
                        }
                        Dialog dialog = transaction.getDialog();
                        if (dialog == null) {
                            log.warn("[发送BYE] transaction is dialog");
                            continue;
                        }
                        //创建客户端，发送请求
                        ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
                        dialog.sendRequest(clientTransaction);
                        // 释放ssrc
                        SipUtil.releaseSsrc(info.getSsrc());
                        // 关闭rtp服务器
                        zlmApiUtils.closeRTPServer(mediaInfo, stream);
                        log.warn("closeRTPServer Port:{}", info.getPort());
                        if (info.isPushing()) {
                            info.setPushing(false);
                        }
                        if (info.isRecording()) {
                            info.setPushing(false);
                        }
                        streamSession.put(info, null);
                        // 删除会话缓存
                        streamSession.remove(info.getDeviceId(), info.getChannelId(), stream, info.getSsrc());
                        // 删除invite缓存
                        inviteService.removeInviteInfo(info.getType(), info.getDeviceId(), info.getChannelId(), info.getStream());
                    }
                }
            } catch (ParseException | SipException | InvalidArgumentException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void streamByeCmd(String deviceId, String channelId, String stream, String ssrc) {
        YfSipDeviceDO dev = sipDeviceService.selectSipDeviceBySipId(deviceId);
        YfSipDeviceRespVO respVO = BeanUtil.toBean(dev, YfSipDeviceRespVO.class);
        if (dev == null) {
            log.error("[发送BYE] device is null");
            return;
        }
        streamByeCmd(respVO, channelId, stream, ssrc);
    }

    private ClientTransaction transmitRequest(Request request) throws SipException {
        log.info("transmitRequest:{}", request);
        ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
        clientTransaction.sendRequest();
        return clientTransaction;
    }

    private String buildRequestContent(IotSipConfigDO sipConfig, YfMediaServerDO mediaInfo, VideoSessionInfo info) {
        String streamMode = info.getStreamMode();
        StringBuilder content = new StringBuilder(200);
        content.append("v=0\r\n");
        switch (info.getType()) {
            case play:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Play\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=0 0\r\n");
                break;
            case playrecord:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Play\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=0 0\r\n");
                break;
            case playback:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Playback\r\n");
                content.append("u=").append(info.getChannelId()).append(":0\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=").append(info.getStartTime()).append(" ").append(info.getEndTime()).append("\r\n");
                break;
            case download:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Download\r\n");
                content.append("u=").append(info.getChannelId()).append(":0\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=").append(info.getStartTime()).append(" ").append(info.getEndTime()).append("\r\n");
                break;
        }
//        if (sipConfig.getSeniorsdp() != null && sipConfig.getSeniorsdp() == 1) {
        if (sipConfig!= null) {
            if ("TCP-PASSIVE".equals(streamMode)) {
                content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
            } else if ("TCP-ACTIVE".equals(streamMode)) {
                content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
            } else if ("UDP".equals(streamMode)) {
                content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 126 125 99 34 98 97\r\n");
            }
            content.append("a=recvonly\r\n");
            content.append("a=rtpmap:96 PS/90000\r\n");
            content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
            content.append("a=rtpmap:126 H264/90000\r\n");
            content.append("a=rtpmap:125 H264S/90000\r\n");
            content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
            content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
            content.append("a=fmtp:99 profile-level-id=3\r\n");
            content.append("a=rtpmap:98 H264/90000\r\n");
            content.append("a=rtpmap:97 MPEG4/90000\r\n");
            if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
                content.append("a=setup:passive\r\n");
                content.append("a=connection:new\r\n");
            } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
                content.append("a=setup:active\r\n");
                content.append("a=connection:new\r\n");
            }
        } else {
            switch (streamMode) {
                case "TCP-PASSIVE":
                    content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 97 98 99\r\n");
                    break;
                case "TCP-ACTIVE":
                    content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 97 98 99\r\n");
                    break;
                case "UDP":
                    //content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 97 98 99\r\n");
                    content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 97 98\r\n");
                    break;
            }
            content.append("a=recvonly\r\n");
            content.append("a=rtpmap:96 PS/90000\r\n");
            content.append("a=rtpmap:97 MPEG4/90000\r\n");
            content.append("a=rtpmap:98 H264/90000\r\n");
            //content.append("a=rtpmap:99 H265/90000\r\n");
            if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
                content.append("a=setup:passive\r\n");
                content.append("a=connection:new\r\n");
            } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
                content.append("a=setup:active\r\n");
                content.append("a=connection:new\r\n");
            }
        }
        if (info.getType() == SessionType.download) {
            content.append("a=downloadspeed:").append(info.getDownloadSpeed()).append("\r\n");
        }
        content.append("y=").append(info.getSsrc()).append("\r\n");// ssrc
        return content.toString();
    }
}
