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

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.media.vo.YfMediaServerPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.media.vo.YfMediaServerSaveReqVO;
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.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.device.YfIotDeviceDO;
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.dal.mysql.yanfan.media.YfMediaServerMapper;
import cn.iocoder.yudao.module.pms.service.yanfan.device.YfIotDeviceService;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.model.ZlmMediaServer;
import com.alibaba.fastjson.JSONObject;
import liquibase.pro.packaged.Y;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;


import javax.annotation.Resource;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * yf流媒体服务器配置 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
@Slf4j
public class YfMediaServerServiceImpl implements YfMediaServerService {

    @Resource
    private YfMediaServerMapper yfMediaServerMapper;
    @Autowired
    private ZlmApiUtils zlmApiUtils;
    @Autowired
    private YfIotDeviceService yfIotDeviceService;
    @Autowired
    private VideoSessionManager streamSession;

    @Override
    public Integer createYfMediaServer(YfMediaServerSaveReqVO createReqVO) {
        // 判断是否为管理员
//        mediaServer.setIsSys(1);
//        SysUser user = getLoginUser().getUser();
////        List<SysRole> roles=user.getRoles();
////        for(int i=0;i<roles.size();i++){
////            if(roles.get(i).getRoleKey().equals("tenant") || roles.get(i).getRoleKey().equals("general")){
////                mediaServer.setIsSys(0);
////                break;
////            }
////        }
//        if (null == user.getDeptId()) {
//            throw new ServiceException("只允许租户配置");
//        }
//        if (!isAdmin(user.getUserId())) {
//            mediaServer.setIsSys(0);
//        }
//        mediaServer.setTenantId(user.getDept().getDeptUserId());
//        mediaServer.setTenantName(user.getDept().getDeptUserName());
        //判断相同ip服务器是否存在
//        MediaServer temp = new MediaServer();
//        temp.setIp(mediaServer.getIp());
//        List<MediaServer> list = mediaServerMapper.selectMediaServerList(temp);

        List<YfMediaServerDO> list = yfMediaServerMapper.selectList("ip", createReqVO.getIp());
        if (CollUtil.isNotEmpty(list)) {
            return 0;
        }
//        mediaServer.setCreateTime(DateUtils.getNowDate());

        YfMediaServerDO mediaServer = BeanUtils.toBean(createReqVO, YfMediaServerDO.class);
        boolean ret = syncMediaServer(mediaServer, mediaServer.getSecret());
        if (ret) {
            mediaServer.setDeleted(false);
            return yfMediaServerMapper.insert(mediaServer);
        } else {
            return 0;
        }
    }


    @Override
    public boolean syncMediaServer(YfMediaServerDO mediaServer, String secret) {
        String hookPrex = String.format("http://%s/admin-api/zlmhook", mediaServer.getHookurl());
        Map<String, Object> param = new HashMap<>();
        param.put("api.secret", secret);

        param.put("hook.enable", "1");
        param.put("general.mediaServerId", mediaServer.getServerId());
        param.put("general.flowThreshold", "64");
        param.put("hook.on_play", String.format("%s/on_play", hookPrex));
        param.put("hook.on_http_access", String.format("%s/on_http_access", hookPrex));
        param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
        param.put("hook.on_server_started", String.format("%s/on_server_started", hookPrex));
        param.put("hook.on_stream_changed", String.format("%s/on_stream_changed", hookPrex));
        param.put("hook.on_stream_none_reader", String.format("%s/on_stream_none_reader", hookPrex));
        param.put("hook.on_stream_not_found", String.format("%s/on_stream_not_found", hookPrex));
        param.put("hook.on_send_rtp_stopped", String.format("%s/on_send_rtp_stopped", hookPrex));
        param.put("hook.on_rtp_server_timeout", String.format("%s/on_rtp_server_timeout", hookPrex));
        param.put("hook.on_flow_report", String.format("%s/on_flow_report", hookPrex));
        param.put("hook.on_server_keepalive", String.format("%s/on_server_keepalive", hookPrex));
        param.put("hook.on_server_exited", String.format("%s/on_server_exited", hookPrex));
        if (Objects.equals(mediaServer.getProtocol(), "http")) {
            param.put("hook.on_record_mp4", String.format("http://127.0.0.1:%s/zlmhook/on_record_mp4", mediaServer.getRecordPort()));
        } else if (Objects.equals(mediaServer.getProtocol(), "https")) {
            param.put("hook.on_record_mp4", String.format("https://%s:%s/zlmhook/on_record_mp4", mediaServer.getDomain(), mediaServer.getRecordPort()));
        }
        param.put("hook.on_record_ts", "");
        param.put("hook.on_rtsp_auth", "");
        param.put("hook.on_rtsp_realm", "");
        param.put("hook.on_shell_login", "");
        param.put("hook.timeoutSec", "20");
        param.put("hook.alive_interval", "60.0");

        param.put("rtsp.port", mediaServer.getPortRtsp().toString());
        param.put("rtmp.port", mediaServer.getPortRtmp().toString());

        param.put("record.appName", "record");
        param.put("record.filePath", "./record");
        if (!ObjectUtils.isEmpty(mediaServer.getRtpPortRange())) {
            param.put("rtp_proxy.port_range", mediaServer.getRtpPortRange().replace(",", "-"));
            log.warn("[ZLM] 修改RTP推流端口时，请同步Docker端口映射");
        }
        JSONObject responseJSON = zlmApiUtils.setServerConfig(mediaServer, param);
        if (responseJSON != null && responseJSON.getInteger("code") == 0) {
            log.info("[ZLM] 设置成功,开始重启以保证配置生效");
            zlmApiUtils.restartServer(mediaServer);
            return true;
        } else {
            log.info("[ZLM] 设置zlm失败 {}", responseJSON);
            return false;
        }
    }

    @Override
    public void updateYfMediaServer(YfMediaServerSaveReqVO updateReqVO) {
        // 校验存在
        validateYfMediaServerExists(updateReqVO.getId());
        // 更新
        YfMediaServerDO updateObj = BeanUtils.toBean(updateReqVO, YfMediaServerDO.class);
        yfMediaServerMapper.updateById(updateObj);
    }

    /**
     * 修改流媒体服务器配置
     *
     * @param mediaServer 流媒体服务器配置
     * @return 结果
     */
    @Caching(evict = {
            @CacheEvict(cacheNames = "mediaServer", key = "'selectMediaServerById' + '_' + #mediaServer.id"),
            @CacheEvict(cacheNames = "mediaServer", key = "'selectMediaServerBytenantId' + '_' + #mediaServer.tenantId"),
    })
    @Override
    public int updateMediaServer(YfMediaServerSaveReqVO mediaServer) {
        YfMediaServerDO bean = BeanUtils.toBean(mediaServer, YfMediaServerDO.class);
        if (mediaServer.getId() != null) {
            String newSecret = mediaServer.getSecret();
            mediaServer.setSecret(yfMediaServerMapper.selectById(mediaServer.getId()).getSecret());
            boolean ret = syncMediaServer(bean, newSecret);
            if (ret) {
                mediaServer.setSecret(newSecret);
                return yfMediaServerMapper.updateById(bean);
            }
        }
        return 0;
    }

    @Override
    public void deleteYfMediaServer(Long id) {
        // 校验存在
        validateYfMediaServerExists(id);
        // 删除
        yfMediaServerMapper.deleteById(id);
    }

    private void validateYfMediaServerExists(Long id) {
        if (yfMediaServerMapper.selectById(id) == null) {
            throw exception(new ErrorCode(1,"不存在"));
        }
    }

    @Override
    public YfMediaServerDO getYfMediaServer(Long id) {
        return yfMediaServerMapper.selectById(id);
    }

    @Override
    public PageResult<YfMediaServerDO> getYfMediaServerPage(YfMediaServerPageReqVO pageReqVO) {
        return yfMediaServerMapper.selectPage(pageReqVO);
    }


    @Override
    public JSONObject listRtpServer() {
        YfMediaServerDO media = selectMediaServerBytenantId(SecurityFrameworkUtils.getLoginUserId());
        if (media != null) {
            return zlmApiUtils.listRtpServer(media).getJSONObject("data");
        }
        return null;
    }

    @Override
    public YfMediaServerDO selectMediaServerBydeviceSipId(String deviceSipId) {
        YfIotDeviceDO device = yfIotDeviceService.selectDeviceBySerialNumber(deviceSipId);
        if (device != null) {
            List<YfMediaServerDO> list = yfMediaServerMapper.selectMediaServerBytenantId(device.getTenantId());
            if (list.size() == 0) {
                return selectMediaServerBytenantId(1L);
            } else if (list.size() == 1) {
                return list.get(0);
            } else {
                //随机选择一个流媒体节点
                Random random = new Random();
                int index = random.nextInt(list.size());
                return list.get(index);
            }
        } else {
            return selectMediaServerBytenantId(1L);
        }

    }


    @Cacheable(cacheNames = "mediaServer", key = "#root.methodName + '_' + #tenantId", unless = "#result == null")
    @Override
    public YfMediaServerDO selectMediaServerBytenantId(Long tenantId) {
        List<YfMediaServerDO> list = yfMediaServerMapper.selectMediaServerBytenantId(tenantId);
        if (list.size() == 0) {
            return selectMediaServerBytenantId(1L);
        } else if (list.size() == 1) {
            return list.get(0);
        } else {
            //随机选择一个流媒体节点
            Random random = new Random();
            int index = random.nextInt(list.size());
            return list.get(index);
        }
    }

    @Override
    public VideoSessionInfo createRTPServer(IotSipConfigDO sipConfig, YfMediaServerDO mediaInfo, YfSipDeviceRespVO device, VideoSessionInfo videoSessionInfo) {
        switch (videoSessionInfo.getType()) {
            case play:
                videoSessionInfo.setSsrc(streamSession.createPlaySsrc(sipConfig.getDomain()));
                videoSessionInfo.setStream(String.format("%s_%s_%s", SipUtil.PREFIX_PLAY, device.getDeviceSipId(), videoSessionInfo.getChannelId()));
                break;
            case playrecord:
                videoSessionInfo.setSsrc(streamSession.createPlaySsrc(sipConfig.getDomain()));
                videoSessionInfo.setStream(String.format("%s_%s_%s", SipUtil.PREFIX_PLAYRECORD, device.getDeviceSipId(), videoSessionInfo.getChannelId()));
                break;
            case playback:
                videoSessionInfo.setSsrc(streamSession.createPlayBackSsrc(sipConfig.getDomain()));
                videoSessionInfo.setStream(videoSessionInfo.getSsrc());
                break;
            case download:
                videoSessionInfo.setSsrc(streamSession.createPlayBackSsrc(sipConfig.getDomain()));
                videoSessionInfo.setStream(videoSessionInfo.getSsrc());
                break;
        }

        int mediaPort = zlmApiUtils.createRTPServer(mediaInfo, videoSessionInfo.getStream(), videoSessionInfo.getSsrc(), false, false, 0);
        videoSessionInfo.setPort(mediaPort);
        return videoSessionInfo;
    }

    @Override
    public YfMediaServerDO checkMediaServer(String ip, Long port, String secret) {
        YfMediaServerDO mediaServerItem = new YfMediaServerDO();
        mediaServerItem.setIp(ip);
        mediaServerItem.setPortHttp(port);
        mediaServerItem.setSecret(secret);
        ZlmMediaServer zlmServerConfig = zlmApiUtils.getMediaServerConfig(mediaServerItem);
        if (zlmServerConfig == null) {
            return null;
        }
        mediaServerItem.setServerId(zlmServerConfig.getMediaServerId());
        mediaServerItem.setPortRtmp(Long.valueOf(zlmServerConfig.getRtmpPort()));
        mediaServerItem.setPortRtsp(Long.valueOf(zlmServerConfig.getRtspPort()));
        mediaServerItem.setRtpProxyPort(Long.valueOf(zlmServerConfig.getRtpProxyPort()));
        mediaServerItem.setHookurl("java:58080");
        return mediaServerItem;
    }

    @Override
    public int deleteMediaServerByIds(Long[] ids) {
        return yfMediaServerMapper.deleteByIds(Arrays.asList(ids));
    }

    /**
     * 查询流媒体服务器配置
     *
     * @return 流媒体服务器配置
     */
    @Override
    public List<YfMediaServerDO> selectMediaServer() {
        YfMediaServerDO mediaServer = new YfMediaServerDO();
//        SysUser user = getLoginUser().getUser();
//        List<SysRole> roles = user.getRoles();
//        for (int i = 0; i < roles.size(); i++) {
//            if (roles.get(i).getRoleKey().equals("tenant")) {
//                // 租户查看第一条流媒体服务器配置
//                mediaServer.setTenantId(user.getUserId());
//            }
//        }
        return yfMediaServerMapper.selectList();
    }
}
