package cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.handler.req;

import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.SipDate;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.handler.IReqHandler;
import cn.iocoder.yudao.module.pms.controller.admin.yanfan.utils.DigestAuthUtil;
import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.device.YfIotDeviceDO;
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.dataobject.yanfan.sip.device.channel.YfSipDeviceChannelDO;
import cn.iocoder.yudao.module.pms.dal.mysql.yanfan.sip.device.YfSipDeviceMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.yanfan.sip.device.channel.YfSipDeviceChannelMapper;
import cn.iocoder.yudao.module.pms.service.yanfan.config.SysSipConfig;
import cn.iocoder.yudao.module.pms.service.yanfan.device.YfIotDeviceService;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.IGBListener;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.IMqttService;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.MessageInvoker;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.config.IotSipConfigService;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.device.YfSipDeviceService;
import cn.iocoder.yudao.module.pms.service.yanfan.sip.device.channel.YfSipDeviceChannelService;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.RateLimiter;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.header.Expires;
import gov.nist.javax.sip.header.SIPDateHeader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.header.*;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class RegisterReqHandler extends ReqAbstractHandler implements InitializingBean, IReqHandler {

    @Autowired
    private YfSipDeviceService sipDeviceService;
    @Autowired
    private YfSipDeviceChannelService sipDeviceChannelService;
    @Autowired
    private YfIotDeviceService yfIotDeviceService;

    @Autowired
    private IotSipConfigService sipConfigService;

    @Autowired
    private IGBListener sipListener;

    @Autowired
    private MessageInvoker messageInvoker;

    @Autowired
    private SysSipConfig sysSipConfig;

    @Autowired
    private IMqttService mqttService;
    @Autowired
    private YfSipDeviceService yfSipDeviceService;
    @Autowired
    private YfSipDeviceMapper yfSipDeviceMapper;
    @Autowired
    private YfSipDeviceChannelMapper yfSipDeviceChannelMapper;

    // 类内定义本地缓存，记录sipId的401请求次数和限流器
    private Cache<String, RateLimiter> sipIdRateLimiterCache = CacheBuilder.newBuilder()
            .expireAfterWrite(5, TimeUnit.MINUTES) // 5分钟过期
            .maximumSize(1000) // 最多缓存1000个设备
            .build();

    @Override
    public void processMsg(RequestEvent evt) {
        TenantUtils.executeIgnore(() -> {
            try {
                log.info("收到注册请求，开始处理");
                Request request = evt.getRequest();

                ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
                String received = viaHeader.getReceived(); // 优先取received（实际接入的IP）
                int rPort = viaHeader.getRPort();

                // 本地模拟设备兼容（received为空/rPort=-1时，用ViaHeader的host/port替代）
                if (StringUtils.isEmpty(received) || rPort == -1) {
                    log.warn("本地地址替代! received:{},rPort:{} [{}:{}]", received, rPort, viaHeader.getHost(), viaHeader.getPort());
                    received = viaHeader.getHost(); // 兜底取ViaHeader中的host作为IP
                    rPort = viaHeader.getPort();
                }

                Response response;
                // 注册标志  0：未携带授权头或者密码错误  1：注册成功   2：注销成功
                int registerFlag;
                FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
                AddressImpl address = (AddressImpl) fromHeader.getAddress();
                SipUri uri = (SipUri) address.getURI();
                String sipId = uri.getUser();
                //取默认Sip配置
                IotSipConfigDO sipConfig = sipConfigService.selectSipConfigBydeviceSipId(sipId);
                if (sipConfig != null) {
                    AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
                    // 校验密码是否正确
                    if (authorhead == null && !ObjectUtils.isEmpty(sipConfig.getPassword())) {
                        // 获取该sipId的限流器（每秒最多打印1条日志，避免刷屏）
                        RateLimiter limiter = sipIdRateLimiterCache.get(sipId, () -> RateLimiter.create(1.0));
                        if (limiter.tryAcquire()) { // 限流控制：每秒仅1次打印日志
                            log.info("未携带授权头 回复401，sipId："+ sipId);
                        }
                        // 401响应逻辑不变
                        response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
                        new DigestAuthUtil().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
                        getServerTransaction(evt).sendResponse(response);
                        return;
                    }

                    boolean pcheck = new DigestAuthUtil().doAuthenticatePlainTextPassword(request,
                            sipConfig.getPassword());

                    boolean syscheck = new DigestAuthUtil().doAuthenticatePlainTextPassword(request,
                            sysSipConfig.getPassword());
                    if (!pcheck && !syscheck) {
                        // 注册失败
                        response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
                        response.setReasonPhrase("wrong password");
                        log.info("[注册请求] 密码/SIP服务器ID错误, 回复403 sipId：" + sipId);
                        getServerTransaction(evt).sendResponse(response);
                        return;
                    }
                    response = getMessageFactory().createResponse(Response.OK, request);
                    // 添加date头
                    SIPDateHeader dateHeader = new SIPDateHeader();
                    // 使用自己修改的
                    SipDate sipDate = new SipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
                    dateHeader.setDate(sipDate);
                    response.addHeader(dateHeader);

                    ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
                    // 添加Contact头
                    response.addHeader(request.getHeader(ContactHeader.NAME));
                    // 添加Expires头
                    response.addHeader(request.getExpires());
//                    ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
//                    String received = viaHeader.getReceived();
//                    int rPort = viaHeader.getRPort();
//                    // 本地模拟设备 received 为空 rPort 为 -1
//                    // 解析本地地址替代
//                    if (StringUtils.isEmpty(received) || rPort == -1) {
//                        log.warn("本地地址替代! received:{},rPort:{} [{}:{}]", received, rPort, viaHeader.getHost(), viaHeader.getPort());
//                        received = viaHeader.getHost();
//                        rPort = viaHeader.getPort();
//                    }
                    YfSipDeviceDO device = new YfSipDeviceDO();
                    ;
                    device.setStreamMode("UDP");
                    device.setDeviceSipId(sipId);
                    device.setIp(received);
                    device.setPort(rPort);
                    device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
                    // 注销成功
                    if (expiresHeader != null && expiresHeader.getExpires() == 0) {
                        registerFlag = 2;
                    } else {
                        registerFlag = 1;
                        // 判断TCP还是UDP
                        boolean isTcp = false;
                        ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
                        String transport = reqViaHeader.getTransport();
                        if (transport.equals("TCP")) {
                            isTcp = true;
                        }
                        device.setTransport(isTcp ? "TCP" : "UDP");
                    }
                    getServerTransaction(evt).sendResponse(response);
                    // 注册成功
                    if (registerFlag == 1) {
                        log.info("注册成功! sipId:" + device.getDeviceSipId());
                        device.setRegisterTime(LocalDateTime.now());
                        yfSipDeviceService.updateDevice(device);
                        List<YfSipDeviceChannelDO> channels = yfSipDeviceChannelMapper.selectList("device_sip_id",device.getDeviceSipId());
                        if (channels.size() > 0) {
                            YfIotDeviceDO iotdev = yfIotDeviceService.selectDeviceBySerialNumber(device.getDeviceSipId());
                            if (iotdev == null) {
                                //添加到统一设备管理 默认添加到admin账号下
                                int result = yfIotDeviceService.insertDeviceAuto(device.getDeviceSipId(), 1L, sipConfig.getProductId());
                                if (result == 1) {
                                    log.info("-----------sip设备认证成功,并自动添加设备到系统，SipId:" + device.getDeviceSipId() + "---------------");
                                }
                                iotdev = new YfIotDeviceDO();
                            }
                            if (iotdev.getStatus() != 3 && iotdev.getStatus() != 2) {
                                iotdev.setStatus(3);
                                yfIotDeviceService.updateDeviceStatusAndLocation(iotdev,device.getIp());
                            }
                            // 重新注册更新设备和通道，以免设备替换或更新后信息无法更新
                            onRegister(device);
                        } else {
                            log.info("未找到改设备! deviceId:" + device.getDeviceId());
                        }
                    } else if (registerFlag == 2) {
                        log.info("注销成功! deviceId:" + device.getDeviceId());
                        mqttService.publishStatus(device, 4);
                    }

                }
            } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        });
    }

    public void onRegister(YfSipDeviceDO device) {
        // TODO 查询设备信息和下挂设备信息 自动拉流
        messageInvoker.deviceInfoQuery(device);
        messageInvoker.catalogQuery(device);
        messageInvoker.subscribe(device,0,0,3600,"0",null,null);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String method = "REGISTER";
        sipListener.addRequestProcessor(method, this);
    }
}
