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

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.service.VideoStreamManager;
import com.usky.cdi.service.sip.sdp.SdpUtils;
import com.usky.cdi.service.sip.util.NetworkUtils;
import gov.nist.javax.sip.RequestEventExt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.nio.charset.StandardCharsets;

@Slf4j
@Component
@ConditionalOnProperty(prefix = "sip.server", name = "enabled", havingValue = "true")
public class SipServerListener implements SipListener {

    private final SipServerProperties properties;
    private final VideoStreamManager streamManager;

    private SipStack sipStack;
    private SipProvider sipProvider;
    private AddressFactory addressFactory;
    private MessageFactory messageFactory;
    private HeaderFactory headerFactory;

    public SipServerListener(SipServerProperties properties, VideoStreamManager streamManager) {
        this.properties = properties;
        this.streamManager = streamManager;
    }

    public void start() throws Exception {
        SipFactory sipFactory = SipFactory.getInstance();
        sipFactory.setPathName("gov.nist");

        java.util.Properties stackProps = new java.util.Properties();
        stackProps.setProperty("javax.sip.STACK_NAME", "UskySipVideoServer");
        stackProps.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0");
        stackProps.setProperty("gov.nist.javax.sip.THREAD_POOL_SIZE", "16");

        sipStack = sipFactory.createSipStack(stackProps);
        addressFactory = sipFactory.createAddressFactory();
        messageFactory = sipFactory.createMessageFactory();
        headerFactory = sipFactory.createHeaderFactory();

        String bindAddress = properties.getHost();
        if (bindAddress == null || bindAddress.isEmpty()) {
            bindAddress = "0.0.0.0";
        }
        String localIp = NetworkUtils.resolveLocalIp(properties.getLocalIp());
        ListeningPoint listeningPoint = sipStack.createListeningPoint(bindAddress, properties.getPort(), "udp");
        sipProvider = sipStack.createSipProvider(listeningPoint);
        sipProvider.addSipListener(this);

        log.info("SIP 服务端已启动: bind={}:{}, localIp={}, platformId={}",
                bindAddress, properties.getPort(), localIp, properties.getPlatformId());
    }

    public void stop() {
        if (sipStack != null) {
            sipStack.stop();
            log.info("SIP 服务端已停止");
        }
    }

    @Override
    public void processRequest(RequestEvent requestEvent) {
        Request request = requestEvent.getRequest();
        String method = request.getMethod();
        log.info("收到 SIP 请求: {} from {}", method, formatRemoteEndpoint(requestEvent));

        try {
            switch (method) {
                case Request.INVITE:
                    handleInvite(requestEvent);
                    break;
                case Request.ACK:
                    handleAck(requestEvent);
                    break;
                case Request.BYE:
                    handleBye(requestEvent);
                    break;
                case Request.REGISTER:
                    handleRegister(requestEvent);
                    break;
                case Request.OPTIONS:
                    handleOptions(requestEvent);
                    break;
                default:
                    sendResponse(requestEvent, Response.NOT_IMPLEMENTED);
            }
        } catch (Exception e) {
            log.error("处理 SIP 请求失败: method={}, error={}", method, e.getMessage(), e);
            try {
                sendResponse(requestEvent, Response.SERVER_INTERNAL_ERROR);
            } catch (Exception ex) {
                log.error("发送错误响应失败", ex);
            }
        }
    }

    private void handleInvite(RequestEvent event) throws Exception {
        Request request = event.getRequest();
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();

        byte[] rawContent = request.getRawContent();
        if (rawContent == null) {
            sendResponse(event, Response.BAD_REQUEST);
            return;
        }
        String sdp = new String(rawContent, StandardCharsets.UTF_8);
        SdpMediaInfo mediaInfo = SdpUtils.parseVideoMedia(sdp);
        if (mediaInfo == null) {
            log.warn("INVITE SDP 中未找到视频媒体描述");
            sendResponse(event, Response.NOT_ACCEPTABLE);
            return;
        }

        String remoteAddress = mediaInfo.getConnectionAddress();
        if (remoteAddress == null) {
            remoteAddress = resolveRemoteIpAddress(event);
            mediaInfo.setConnectionAddress(remoteAddress);
        }

        VideoStreamSession session = streamManager.createSession(callId, remoteAddress, mediaInfo);
        String answerSdp = streamManager.buildAnswerSdp(session);

        Response okResponse = messageFactory.createResponse(Response.OK, request);
        ToHeader toHeader = (ToHeader) okResponse.getHeader(ToHeader.NAME);
        toHeader.setTag(generateTag());

        String localIp = NetworkUtils.resolveLocalIp(properties.getLocalIp());
        SipURI contactUri = addressFactory.createSipURI(properties.getPlatformId(), localIp);
        contactUri.setPort(properties.getPort());
        Address contactAddress = addressFactory.createAddress(contactUri);
        ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
        okResponse.addHeader(contactHeader);

        ContentTypeHeader contentType = headerFactory.createContentTypeHeader("application", "sdp");
        okResponse.setContent(answerSdp, contentType);

        ServerTransaction serverTransaction = event.getServerTransaction();
        if (serverTransaction == null) {
            serverTransaction = sipProvider.getNewServerTransaction(request);
        }
        serverTransaction.sendResponse(okResponse);
        log.info("已应答 INVITE: callId={}, localRtpPort={}", callId, session.getLocalRtpPort());
    }

    private void handleAck(RequestEvent event) {
        Request request = event.getRequest();
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();
        VideoStreamSession session = streamManager.getSession(callId);
        if (session != null) {
            streamManager.startReceiving(session);
            log.info("收到 ACK，开始接收视频流: callId={}", callId);
        }
    }

    private void handleBye(RequestEvent event) throws Exception {
        Request request = event.getRequest();
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();
        streamManager.stopSession(callId);
        sendResponse(event, Response.OK);
    }

    private void handleRegister(RequestEvent event) throws Exception {
        Response okResponse = messageFactory.createResponse(Response.OK, event.getRequest());
        ToHeader toHeader = (ToHeader) okResponse.getHeader(ToHeader.NAME);
        toHeader.setTag(generateTag());

        ExpiresHeader expiresHeader = headerFactory.createExpiresHeader(3600);
        okResponse.addHeader(expiresHeader);

        ContactHeader contactHeader = (ContactHeader) event.getRequest().getHeader(ContactHeader.NAME);
        if (contactHeader != null) {
            okResponse.addHeader(contactHeader);
        }

        ServerTransaction st = event.getServerTransaction();
        if (st == null) {
            st = sipProvider.getNewServerTransaction(event.getRequest());
        }
        st.sendResponse(okResponse);
        log.info("设备注册成功: from={}", formatRemoteEndpoint(event));
    }

    private void handleOptions(RequestEvent event) throws Exception {
        Response okResponse = messageFactory.createResponse(Response.OK, event.getRequest());
        okResponse.addHeader(headerFactory.createAllowHeader(
                "INVITE, ACK, BYE, CANCEL, OPTIONS, REGISTER"));
        sendResponse(event, okResponse);
    }

    private void sendResponse(RequestEvent event, int statusCode) throws Exception {
        Response response = messageFactory.createResponse(statusCode, event.getRequest());
        sendResponse(event, response);
    }

    private void sendResponse(RequestEvent event, Response response) throws Exception {
        if (response.getStatusCode() >= 200) {
            ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
            if (toHeader.getTag() == null) {
                toHeader.setTag(generateTag());
            }
        }
        ServerTransaction st = event.getServerTransaction();
        if (st == null) {
            st = sipProvider.getNewServerTransaction(event.getRequest());
        }
        st.sendResponse(response);
    }

    private String generateTag() {
        return Long.toHexString(System.nanoTime());
    }

    private String resolveRemoteIpAddress(RequestEvent event) {
        if (event instanceof RequestEventExt) {
            String ip = ((RequestEventExt) event).getRemoteIpAddress();
            if (ip != null && !ip.isEmpty()) {
                return ip;
            }
        }
        Request request = event.getRequest();
        ViaHeader via = (ViaHeader) request.getHeader(ViaHeader.NAME);
        if (via != null && via.getHost() != null) {
            return via.getHost();
        }
        FromHeader from = (FromHeader) request.getHeader(FromHeader.NAME);
        if (from != null && from.getAddress() != null && from.getAddress().getURI() instanceof SipURI) {
            return ((SipURI) from.getAddress().getURI()).getHost();
        }
        return "unknown";
    }

    private String formatRemoteEndpoint(RequestEvent event) {
        if (event instanceof RequestEventExt) {
            RequestEventExt ext = (RequestEventExt) event;
            String ip = ext.getRemoteIpAddress();
            int port = ext.getRemotePort();
            if (ip != null && !ip.isEmpty()) {
                return port > 0 ? ip + ":" + port : ip;
            }
        }
        return resolveRemoteIpAddress(event);
    }

    @Override
    public void processResponse(ResponseEvent responseEvent) {
        log.debug("收到 SIP 响应: {}", responseEvent.getResponse().getStatusCode());
    }

    @Override
    public void processTimeout(TimeoutEvent timeoutEvent) {
        log.warn("SIP 超时: event={}", timeoutEvent.getTimeout());
    }

    @Override
    public void processIOException(IOExceptionEvent exceptionEvent) {
        log.warn("SIP IO 异常: host={}, port={}",
                exceptionEvent.getHost(), exceptionEvent.getPort());
    }

    @Override
    public void processTransactionTerminated(TransactionTerminatedEvent event) {
        log.debug("SIP 事务终止");
    }

    @Override
    public void processDialogTerminated(DialogTerminatedEvent event) {
        Dialog dialog = event.getDialog();
        if (dialog != null && dialog.getCallId() != null) {
            CallIdHeader callIdHeader = dialog.getCallId();
            if (callIdHeader != null) {
                streamManager.stopSession(callIdHeader.getCallId());
            }
        }
        log.debug("SIP 对话终止");
    }
}
