package com.usky.cdi.service.sip.service;

import com.usky.cdi.service.sip.config.SipServerProperties;
import com.usky.cdi.service.sip.model.SdpMediaInfo;
import com.usky.cdi.service.sip.model.VideoStreamSession;
import com.usky.cdi.service.sip.rtp.RtpVideoReceiver;
import com.usky.cdi.service.sip.sdp.SdpUtils;
import com.usky.cdi.service.sip.util.NetworkUtils;
import com.usky.cdi.service.sip.util.PortAllocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
@ConditionalOnProperty(prefix = "sip.server", name = "enabled", havingValue = "true")
public class VideoStreamManager {

    private final SipServerProperties properties;
    private final PortAllocator portAllocator;
    private final Map<String, VideoStreamSession> sessions = new ConcurrentHashMap<>();
    private final Map<String, RtpVideoReceiver> receivers = new ConcurrentHashMap<>();

    public VideoStreamManager(SipServerProperties properties, PortAllocator portAllocator) {
        this.properties = properties;
        this.portAllocator = portAllocator;
    }

    public VideoStreamSession createSession(String callId, String remoteAddress, SdpMediaInfo mediaInfo) {
        String sessionId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
        int localRtpPort = portAllocator.allocate();

        VideoStreamSession session = new VideoStreamSession();
        session.setSessionId(sessionId);
        session.setCallId(callId);
        session.setRemoteAddress(remoteAddress);
        session.setRemoteRtpPort(mediaInfo.getPort());
        session.setLocalRtpPort(localRtpPort);
        session.setCodec(mediaInfo.getCodec());
        session.setPayloadType(mediaInfo.getPayloadType());
        session.setStartTime(LocalDateTime.now());
        session.setState(VideoStreamSession.SessionState.INIT);

        sessions.put(callId, session);
        log.info("创建视频会话: callId={}, sessionId={}, remote={}:{}, localRtpPort={}, codec={}",
                callId, sessionId, remoteAddress, mediaInfo.getPort(), localRtpPort, mediaInfo.getCodec());
        return session;
    }

    public String buildAnswerSdp(VideoStreamSession session) {
        String localIp = NetworkUtils.resolveLocalIp(properties.getLocalIp());
        return SdpUtils.buildVideoAnswerSdp(localIp, session.getLocalRtpPort(),
                session.getPayloadType(), session.getCodec());
    }

    public void startReceiving(VideoStreamSession session) {
        try {
            boolean psStream = isPsCodec(session.getCodec());
            RtpVideoReceiver receiver = new RtpVideoReceiver(
                    session,
                    session.getLocalRtpPort(),
                    psStream,
                    null,
                    null
            );
            receiver.start();
            receivers.put(session.getCallId(), receiver);
            session.setState(VideoStreamSession.SessionState.ESTABLISHED);
        } catch (IOException e) {
            log.error("启动视频接收失败: callId={}, error={}", session.getCallId(), e.getMessage(), e);
            stopSession(session.getCallId());
        }
    }

    public void stopSession(String callId) {
        RtpVideoReceiver receiver = receivers.remove(callId);
        if (receiver != null) {
            receiver.stop();
        }
        VideoStreamSession session = sessions.get(callId);
        if (session != null) {
            session.setState(VideoStreamSession.SessionState.STOPPED);
        }
        log.info("视频会话已结束: callId={}", callId);
    }

    public VideoStreamSession getSession(String callId) {
        return sessions.get(callId);
    }

    public Collection<VideoStreamSession> getAllSessions() {
        return sessions.values();
    }

    private boolean isPsCodec(String codec) {
        if (codec == null) {
            return false;
        }
        String upper = codec.toUpperCase();
        return upper.contains("PS") || upper.contains("MPEG");
    }
}
