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

import cn.skcks.docking.gb28181.common.json.ResponseStatus;
import cn.skcks.docking.gb28181.common.xml.XmlUtils;
import cn.skcks.docking.gb28181.core.sip.gb28181.constant.CmdType;
import cn.skcks.docking.gb28181.core.sip.gb28181.constant.GB28181Constant;
import cn.skcks.docking.gb28181.core.sip.listener.SipListener;
import cn.skcks.docking.gb28181.core.sip.message.processor.MessageProcessor;
import cn.skcks.docking.gb28181.core.sip.message.processor.message.request.dto.MessageDTO;
import cn.skcks.docking.gb28181.core.sip.message.sender.SipMessageSender;
import cn.skcks.docking.gb28181.core.sip.message.subscribe.GenericSubscribe;
import cn.skcks.docking.gb28181.core.sip.utils.SipUtil;
import cn.skcks.docking.gb28181.sip.manscdp.recordinfo.response.RecordInfoResponseDTO;
import cn.skcks.docking.gb28181.wvp.service.docking.DockingService;
import cn.skcks.docking.gb28181.wvp.sip.message.message.catalog.dto.CatalogResponseDTO;
import cn.skcks.docking.gb28181.wvp.sip.message.message.notify.MediaStatusRequestDTO;
import cn.skcks.docking.gb28181.wvp.sip.request.SipRequestBuilder;
import cn.skcks.docking.gb28181.wvp.sip.response.SipResponseBuilder;
import cn.skcks.docking.gb28181.wvp.sip.sender.SipSender;
import cn.skcks.docking.gb28181.wvp.sip.subscribe.SipSubscribe;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.sip.RequestEvent;
import javax.sip.address.SipURI;
import javax.sip.header.CallIdHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.util.EventObject;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequiredArgsConstructor
@Component
public class MessageRequestProcessor implements MessageProcessor {
    private final SipListener sipListener;
    private final SipMessageSender sender;
    private final SipSender sipSender;
    private final SipSubscribe subscribe;
    private final DockingService dockingService;

    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

    @PostConstruct
    @Override
    public void init() {
        sipListener.addRequestProcessor(Request.MESSAGE,this);
    }

    @Override
    public void process(EventObject eventObject) {
        RequestEvent requestEvent = (RequestEvent) eventObject;
        SIPRequest request = (SIPRequest)requestEvent.getRequest();
        String deviceId = SipUtil.getUserIdFromFromHeader(request);
        CallIdHeader callIdHeader = request.getCallIdHeader();

        byte[] content = request.getRawContent();
        MessageDTO messageDto = XmlUtils.parse(content, MessageDTO.class, GB28181Constant.CHARSET);
        log.debug("接收到的消息 => {}", messageDto);

        String senderIp = request.getLocalAddress().getHostAddress();

        if(!dockingService.hasDeviceByDeviceCode(deviceId)){
            log.info("未找到相关设备信息 => {}", deviceId);
            Response response = response(request,Response.NOT_FOUND,"设备未注册");
            sender.send(senderIp,response);
            return;
        }

        Response ok = response(request, Response.OK, "OK");
        Response response;

        if(StringUtils.equalsAnyIgnoreCase(messageDto.getCmdType(), CmdType.KEEPALIVE)){
            response = ok;
            // 更新设备在线状态
        }  else if(messageDto.getCmdType().equalsIgnoreCase(cn.skcks.docking.gb28181.constant.CmdType.RECORD_INFO)) {
            response = ok;
            RecordInfoResponseDTO dto = XmlUtils.parse(content, RecordInfoResponseDTO.class, GB28181Constant.CHARSET);
            String key = GenericSubscribe.Helper.getKey(cn.skcks.docking.gb28181.constant.CmdType.RECORD_INFO, dto.getDeviceId(), dto.getSn());
            Optional.ofNullable(subscribe.getMessageSubscribe().getPublisher(key))
                    .ifPresentOrElse(publisher -> publisher.submit(request),
                            () -> log.warn("对应订阅 {} 已结束, 异常数据 => {}", key, dto));
        } else if(messageDto.getCmdType().equalsIgnoreCase(CmdType.CATALOG)){
            response = ok;
            CatalogResponseDTO dto = XmlUtils.parse(content, CatalogResponseDTO.class, GB28181Constant.CHARSET);
            String key = GenericSubscribe.Helper.getKey(CmdType.CATALOG, dto.getDeviceId());
            Optional.ofNullable(subscribe.getCatalogSubscribe().getPublisher(key))
                    .ifPresentOrElse(publisher-> publisher.submit(request),
                            ()-> log.warn("对应订阅 {} 已结束, 异常数据 => {}",key, dto));
        } else if(messageDto.getCmdType().equalsIgnoreCase(CmdType.MEDIA_STATUS)){
            response = ok;

            MediaStatusRequestDTO parse = XmlUtils.parse(content, MediaStatusRequestDTO.class, GB28181Constant.CHARSET);
            if(StringUtils.equalsIgnoreCase(parse.getNotifyType(),"121")){
                String ip = request.getLocalAddress().getHostAddress();
                String transport = request.getTopmostViaHeader().getTransport();
                scheduledExecutorService.schedule(()->{
                    sipSender.sendResponse(ip, transport, ((provider, ip1, port) ->
                            SipResponseBuilder.response(request, Response.OK, "OK")));

                    String targetIp = request.getRemoteAddress().getHostAddress();
                    int targetPort = request.getRemotePort();
                    String targetId = ((SipURI)request.getFromHeader().getAddress().getURI()).getUser();
                    String callId = request.getCallIdHeader().getCallId();
                    Request byeRequest = SipRequestBuilder.createByeRequest(targetIp, targetPort, targetId, SipUtil.generateFromTag(), null, callId);
                    sipSender.sendRequest(((provider, ip1, port) -> byeRequest));
                },100, TimeUnit.MILLISECONDS);
            }
        }else {
            response = response(request, Response.NOT_IMPLEMENTED, ResponseStatus.NOT_IMPLEMENTED.getMessage());
        }
        sender.send(senderIp, response);
    }

    @SneakyThrows
    public Response response(SIPRequest request, int status, String message){
        if (request.getToHeader().getTag() == null) {
            request.getToHeader().setTag(SipUtil.generateTag());
        }
        SIPResponse response = (SIPResponse)getMessageFactory().createResponse(status, request);
        if (message != null) {
            response.setReasonPhrase(message);
        }
        return  response;
    }
}
