package cn.skcks.docking.gb28181.wvp.sip.request;

import cn.skcks.docking.gb28181.common.redis.RedisUtil;
import cn.skcks.docking.gb28181.core.sip.gb28181.cache.CacheUtil;
import cn.skcks.docking.gb28181.core.sip.gb28181.constant.GB28181Constant;
import cn.skcks.docking.gb28181.core.sip.message.MessageHelper;
import cn.skcks.docking.gb28181.core.sip.utils.SipUtil;
import cn.skcks.docking.gb28181.wvp.config.ProxySipConfig;
import cn.skcks.docking.gb28181.wvp.orm.mybatis.dynamic.model.WvpProxyDocking;
import gov.nist.javax.sip.message.MessageFactoryImpl;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.sip.SipFactory;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@SuppressWarnings("Duplicates")
@DependsOn("proxySipConfig")
@Component
public class SipRequestBuilder implements ApplicationContextAware {
    private static ProxySipConfig sipConfig;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        sipConfig = applicationContext.getBean(ProxySipConfig.class);
    }

    private static SipFactory getSipFactory(){
        return SipFactory.getInstance();
    }

    @SneakyThrows
    private static List<ViaHeader> getViaHeaders(String ip,int port, String transport, String viaTag){
        ViaHeader viaHeader = getSipFactory().createHeaderFactory().createViaHeader(ip, port, transport, viaTag);
        viaHeader.setRPort();
        return Collections.singletonList(viaHeader);
    }

    @SneakyThrows
    private static CSeqHeader getCSeqHeader(long cSeq, String method){
        return getSipFactory().createHeaderFactory().createCSeqHeader(cSeq, method);
    }

    @SneakyThrows
    public static Request createMessageRequest(WvpProxyDocking device, String ip, int port, long cSeq, String content, String fromTag, CallIdHeader callIdHeader) {
        Request request;
        String target = StringUtils.joinWith(":", device.getIp(), device.getPort());
        // sip uri
        SipURI requestURI = MessageHelper.createSipURI(device.getGbDeviceId(), target);

        // via
        List<ViaHeader> viaHeaders = getViaHeaders(ip, port, sipConfig.getTransport(), null );

        String from = StringUtils.joinWith(":", ip, port);
        // from
        SipURI fromSipURI = MessageHelper.createSipURI(device.getGbDeviceId(), from);
        Address fromAddress = MessageHelper.createAddress(fromSipURI);
        FromHeader fromHeader = MessageHelper.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = MessageHelper.createSipURI(device.getGbDeviceId(), target);
        Address toAddress = MessageHelper.createAddress(toSipURI);
        ToHeader toHeader = MessageHelper.createToHeader(toAddress, null);

        // Forwards
        MaxForwardsHeader maxForwards = MessageHelper.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = getSipFactory().createHeaderFactory().createCSeqHeader(cSeq, Request.MESSAGE);

        // 使用 GB28181 默认编码 否则中文将会乱码
        MessageFactoryImpl messageFactory = (MessageFactoryImpl) getSipFactory().createMessageFactory();
        messageFactory.setDefaultContentEncodingCharset(GB28181Constant.CHARSET);
        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);

        request.addHeader(SipUtil.createUserAgentHeader());

        ContentTypeHeader contentTypeHeader = getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    @SneakyThrows
    public static Request createMessageRequest(WvpProxyDocking device, String ip, int port,long cSeq,String content, String viaTag, String fromTag, CallIdHeader callIdHeader) {
        Request request;
        String target = StringUtils.joinWith(":", device.getIp(), device.getPort());
        // sip uri
        SipURI requestURI = MessageHelper.createSipURI(device.getGbDeviceId(), target);

        // via
        List<ViaHeader> viaHeaders = getViaHeaders(ip, port, sipConfig.getTransport(), viaTag );

        String from = StringUtils.joinWith(":", ip, port);
        // from
        SipURI fromSipURI = MessageHelper.createSipURI(sipConfig.getId(), from);
        Address fromAddress = MessageHelper.createAddress(fromSipURI);
        FromHeader fromHeader = MessageHelper.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = MessageHelper.createSipURI(device.getGbDeviceId(), target);
        Address toAddress = MessageHelper.createAddress(toSipURI);
        ToHeader toHeader = MessageHelper.createToHeader(toAddress, null);

        // Forwards
        MaxForwardsHeader maxForwards = MessageHelper.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = getSipFactory().createHeaderFactory().createCSeqHeader(cSeq, Request.MESSAGE);

        // 使用 GB28181 默认编码 否则中文将会乱码
        MessageFactoryImpl messageFactory = (MessageFactoryImpl) getSipFactory().createMessageFactory();
        messageFactory.setDefaultContentEncodingCharset(GB28181Constant.CHARSET);
        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);

        request.addHeader(SipUtil.createUserAgentHeader());

        ContentTypeHeader contentTypeHeader = getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    @SneakyThrows
    public static Request createInviteRequest(String ip, int port, WvpProxyDocking device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader){
        Request request;
        String target = StringUtils.joinWith(":", device.getIp(), device.getPort());
        SipURI requestLine = MessageHelper.createSipURI(channelId, target);
        // via
        List<ViaHeader> viaHeaders = getViaHeaders(ip, port, sipConfig.getTransport(), viaTag );
        // from
        SipURI fromSipURI = MessageHelper.createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = MessageHelper.createAddress(fromSipURI);
        FromHeader fromHeader = MessageHelper.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = MessageHelper.createSipURI(channelId, target);
        Address toAddress = MessageHelper.createAddress(toSipURI);
        ToHeader toHeader =  MessageHelper.createToHeader(toAddress, null);
        // Forwards
        MaxForwardsHeader maxForwards = getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
        // cSeq
        CSeqHeader cSeqHeader = getSipFactory().createHeaderFactory().createCSeqHeader(getCSeq(), Request.INVITE);
        request = getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);
        request.addHeader(SipUtil.createUserAgentHeader());

        Address concatAddress = MessageHelper.createAddress(MessageHelper.createSipURI(sipConfig.getId(), ip + ":" + port));

        request.addHeader(getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
        // Subject
        SubjectHeader subjectHeader = getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
        request.addHeader(subjectHeader);
        request.addHeader(SipUtil.createUserAgentHeader());

        ContentTypeHeader contentTypeHeader = getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    @SneakyThrows
    public static Request createAckRequest(int status,String ip, int port, WvpProxyDocking device, String channelId, String fromTag, String toTag, String callId) {
        Request request;
        // 请求行
        String target = StringUtils.joinWith(":", device.getIp(), device.getPort());
        SipURI requestLine = MessageHelper.createSipURI(channelId, target);
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = getSipFactory().createHeaderFactory().createViaHeader(ip, port, sipConfig.getTransport(), SipUtil.generateViaTag());
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = MessageHelper.createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = MessageHelper.createAddress(fromSipURI);
        FromHeader fromHeader = MessageHelper.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = MessageHelper.createSipURI(channelId, target);
        Address toAddress = MessageHelper.createAddress(toSipURI);
        ToHeader toHeader = MessageHelper.createToHeader(toAddress, toTag);

        // Forwards
        MaxForwardsHeader maxForwards = getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);

        // ceq
        CSeqHeader cSeqHeader = getSipFactory().createHeaderFactory().createCSeqHeader(getCSeq(), Request.ACK);
        CallIdHeader callIdHeader = getSipFactory().createHeaderFactory().createCallIdHeader(callId);
        request = getSipFactory().createMessageFactory().createRequest(requestLine, Request.ACK, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

        request.addHeader(SipUtil.createUserAgentHeader());

        Address concatAddress = MessageHelper.createAddress(MessageHelper.createSipURI(sipConfig.getId(), ip + ":" + port));
        request.addHeader(getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
        request.addHeader(SipUtil.createUserAgentHeader());

        return request;
    }

    @SneakyThrows
    public static Request createByeRequest(String ip, int port, WvpProxyDocking device, String channelId, String fromTag, String toTag, String callId) {
        Request request;
        // 请求行
        String target = StringUtils.joinWith(":", device.getIp(), device.getPort());
        SipURI requestLine = MessageHelper.createSipURI(channelId, target);
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = getSipFactory().createHeaderFactory().createViaHeader(ip, port, sipConfig.getTransport(), SipUtil.generateViaTag());
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = MessageHelper.createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = MessageHelper.createAddress(fromSipURI);
        FromHeader fromHeader = MessageHelper.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = MessageHelper.createSipURI(channelId, target);
        Address toAddress = MessageHelper.createAddress(toSipURI);
        ToHeader toHeader = MessageHelper.createToHeader(toAddress, toTag);

        // Forwards
        MaxForwardsHeader maxForwards = getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);

        // ceq
        CSeqHeader cSeqHeader = getSipFactory().createHeaderFactory().createCSeqHeader(getCSeq(), Request.BYE);
        CallIdHeader callIdHeader = getSipFactory().createHeaderFactory().createCallIdHeader(callId);
        request = getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

        request.addHeader(SipUtil.createUserAgentHeader());

        Address concatAddress = MessageHelper.createAddress(MessageHelper.createSipURI(sipConfig.getId(), ip + ":" + port));
        request.addHeader(getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
        request.addHeader(SipUtil.createUserAgentHeader());

        return request;
    }

    @SneakyThrows
    public static Request createByeRequest(String ip, int port, long cSeq, String targetId, String fromTag, String toTag, String callId) {
        Request request;
        // 请求行
        String target = StringUtils.joinWith(":", ip, port);
        SipURI requestLine = MessageHelper.createSipURI(targetId, target);
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = getSipFactory().createHeaderFactory().createViaHeader(ip, port, sipConfig.getTransport(), SipUtil.generateViaTag());
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = MessageHelper.createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = MessageHelper.createAddress(fromSipURI);
        FromHeader fromHeader = MessageHelper.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = MessageHelper.createSipURI(targetId, target);
        Address toAddress = MessageHelper.createAddress(toSipURI);
        ToHeader toHeader = MessageHelper.createToHeader(toAddress, toTag);

        // Forwards
        MaxForwardsHeader maxForwards = getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);

        // ceq
        CSeqHeader cSeqHeader = getSipFactory().createHeaderFactory().createCSeqHeader(cSeq, Request.BYE);
        CallIdHeader callIdHeader = getSipFactory().createHeaderFactory().createCallIdHeader(callId);
        request = getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

        request.addHeader(SipUtil.createUserAgentHeader());

        Address concatAddress = MessageHelper.createAddress(MessageHelper.createSipURI(sipConfig.getId(), ip + ":" + port));
        request.addHeader(getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
        request.addHeader(SipUtil.createUserAgentHeader());

        return request;
    }

    @SneakyThrows
    public static Request createByeRequest(String ip, int port, String targetId, String fromTag, String toTag, String callId) {
        Request request;
        // 请求行
        String target = StringUtils.joinWith(":", ip, port);
        SipURI requestLine = MessageHelper.createSipURI(targetId, target);
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = getSipFactory().createHeaderFactory().createViaHeader(ip, port, sipConfig.getTransport(), SipUtil.generateViaTag());
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = MessageHelper.createSipURI(sipConfig.getId(), sipConfig.getDomain());
        Address fromAddress = MessageHelper.createAddress(fromSipURI);
        FromHeader fromHeader = MessageHelper.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = MessageHelper.createSipURI(targetId, target);
        Address toAddress = MessageHelper.createAddress(toSipURI);
        ToHeader toHeader = MessageHelper.createToHeader(toAddress, toTag);

        // Forwards
        MaxForwardsHeader maxForwards = getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);

        // ceq
        CSeqHeader cSeqHeader = getSipFactory().createHeaderFactory().createCSeqHeader(getCSeq(), Request.BYE);
        CallIdHeader callIdHeader = getSipFactory().createHeaderFactory().createCallIdHeader(callId);
        request = getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);

        request.addHeader(SipUtil.createUserAgentHeader());

        Address concatAddress = MessageHelper.createAddress(MessageHelper.createSipURI(sipConfig.getId(), ip + ":" + port));
        request.addHeader(getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
        request.addHeader(SipUtil.createUserAgentHeader());

        return request;
    }

    public static long getCSeq() {
        String key = CacheUtil.getKey(CacheUtil.SIP_C_SEQ_PREFIX,sipConfig.getId());

        long result = 1L;
        if(RedisUtil.KeyOps.hasKey(key)){
            try {
                result =  RedisUtil.StringOps.incrBy(key,1L);
            } finally {
                if (result > Integer.MAX_VALUE) {
                    RedisUtil.StringOps.set(key, String.valueOf(1L));
                    result = 1L;
                }
            }
        } else {
            RedisUtil.StringOps.set(key, String.valueOf(result));
        }

        return result;
    }
}
