package io.github.lunasaw.gbproxy.test;

import io.github.lunasaw.gbproxy.client.Gb28181Client;
import io.github.lunasaw.gbproxy.client.transmit.cmd.ClientCommandSender;
import io.github.lunasaw.gbproxy.server.Gb28181Server;
import io.github.lunasaw.gbproxy.test.config.TestDeviceSupplier;
import io.github.lunasaw.sip.common.entity.FromDevice;
import io.github.lunasaw.sip.common.entity.ToDevice;
import io.github.lunasaw.sip.common.layer.SipLayer;
import io.github.lunasaw.sip.common.service.DeviceSupplier;
import io.github.lunasaw.sip.common.transmit.SipSender;
import io.github.lunasaw.sip.common.transmit.event.Event;
import io.github.lunasaw.sip.common.transmit.event.EventResult;

import io.github.lunasaw.sip.common.utils.SipRequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * SIP代理集成测试类
 * 测试SIP协议栈、设备管理、消息传输等核心功能
 *
 * @author luna
 * @date 2025/01/23
 */
@Slf4j
@SpringBootTest(classes = Gb28181ApplicationTest.class)
@TestPropertySource(properties = {
    "sip.gb28181.server.ip=0.0.0.0",
    "sip.gb28181.server.port=5060",
    "sip.gb28181.server.domain=34020000002000000001",
    "sip.gb28181.server.serverId=34020000002000000001",
    "sip.gb28181.server.serverName=GB28181-Server",
    "sip.gb28181.server.maxDevices=1000",
    "sip.gb28181.server.deviceTimeout=5m",
    "sip.gb28181.server.enableTcp=true",
    "sip.gb28181.server.enableUdp=true",

    "sip.gb28181.client.keepAliveInterval=1m",
    "sip.gb28181.client.maxRetries=3",
    "sip.gb28181.client.retryDelay=5s",
    "sip.gb28181.client.registerExpires=3600",
    "sip.gb28181.client.clientId=34020000001320000001",
    "sip.gb28181.client.clientName=GB28181-Client",
    "sip.gb28181.client.username=admin",
    "sip.gb28181.client.password=123456",

    "sip.performance.messageQueueSize=1000",
    "sip.performance.threadPoolSize=200",
    "sip.performance.enableMetrics=true",
    "sip.performance.enableAsync=true",

    "sip.async.enabled=true",
    "sip.async.corePoolSize=4",
    "sip.async.maxPoolSize=8",

    "sip.cache.enabled=true",
    "sip.cache.maxSize=1000",
    "sip.cache.expireAfterWrite=1h",

    "sip.pool.enabled=true",
    "sip.pool.maxConnections=100",
    "sip.pool.coreConnections=10"
})
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SipProxyIntegrationTest {

    @Autowired
    private SipLayer           sipLayer;
    @Autowired
    private TestDeviceSupplier testDeviceSupplier;

    private CountDownLatch responseLatch;

    @BeforeEach
    public void setUp() {
        // 获取客户端设备
        FromDevice clientFromDevice = testDeviceSupplier.getClientFromDevice();
        if (clientFromDevice == null) {
            log.error("未找到客户端设备配置");
            return;
        }

        // 设置监听点
        responseLatch = new CountDownLatch(1);
    }

    @Test
    @Order(1)
    @DisplayName("测试SIP层初始化")
    public void testSipLayerInitialization() {
        Assertions.assertNotNull(sipLayer, "SIP层应该被正确初始化");

        // 获取设备
        FromDevice clientFromDevice = testDeviceSupplier.getClientFromDevice();
        ToDevice clientToDevice = testDeviceSupplier.getClientToDevice();

        Assertions.assertNotNull(clientFromDevice, "客户端设备应该被正确配置");
        Assertions.assertNotNull(clientToDevice, "服务端设备应该被正确配置");

        System.out.println("=== SIP层初始化测试通过 ===");
        System.out.println("客户端设备: " + clientFromDevice.getUserId() + ":" + clientFromDevice.getPort());
        System.out.println("服务端设备: " + clientToDevice.getUserId() + ":" + clientToDevice.getPort());
        sipLayer.addListeningPoint(clientFromDevice.getIp(), clientFromDevice.getPort());

    }

    @Test
    @Order(2)
    @DisplayName("测试设备注册")
    public void testDeviceRegistration() throws Exception {
        // 获取设备
        FromDevice clientFromDevice = testDeviceSupplier.getClientFromDevice();
        ToDevice clientToDevice = testDeviceSupplier.getClientToDevice();

        if (clientFromDevice == null || clientToDevice == null) {
            log.error("未找到设备配置");
            return;
        }

        String resultCallId = ClientCommandSender.sendRegisterCommand(clientFromDevice, clientToDevice, 3600);

        System.out.println("=== 开始设备注册测试 ===");
        System.out.println("注册请求CallId: " + resultCallId);
        System.out.println("注册请求From: " + clientFromDevice.getUserId());
        System.out.println("注册请求To: " + clientToDevice.getUserId());

        // 在测试环境中，我们只验证请求能够发送
        Assertions.assertNotNull(resultCallId, "注册请求已发送");
    }

    @Test
    @Order(3)
    @DisplayName("测试设备心跳")
    public void testDeviceHeartbeat() throws Exception {
        // 获取设备
        FromDevice clientFromDevice = testDeviceSupplier.getClientFromDevice();
        ToDevice clientToDevice = testDeviceSupplier.getClientToDevice();

        if (clientFromDevice == null || clientToDevice == null) {
            log.error("未找到设备配置");
            return;
        }

        String heartbeatContent = "<?xml version=\"1.0\"?>\n" +
                "<Control>\n" +
                "  <CmdType>Keepalive</CmdType>\n" +
                "  <SN>1</SN>\n" +
                "  <DeviceID>" + clientFromDevice.getUserId() + "</DeviceID>\n" +
                "</Control>";

        System.out.println("=== 开始设备心跳测试 ===");

        String resultCallId = ClientCommandSender.sendKeepaliveCommand(clientFromDevice, clientToDevice, "onLine");

        System.out.println("心跳请求CallId: " + resultCallId);

        // 心跳测试主要验证请求能够发送
        Assertions.assertNotNull(resultCallId, "心跳请求已发送");
    }

    @Test
    @Order(4)
    @DisplayName("测试设备目录查询")
    public void testDeviceCatalogQuery() throws Exception {
        // 获取设备
        FromDevice clientFromDevice = testDeviceSupplier.getClientFromDevice();
        ToDevice clientToDevice = testDeviceSupplier.getClientToDevice();

        if (clientFromDevice == null || clientToDevice == null) {
            log.error("未找到设备配置");
            return;
        }

        String catalogContent = "<?xml version=\"1.0\"?>\n" +
                "<Query>\n" +
                "  <CmdType>Catalog</CmdType>\n" +
                "  <SN>2</SN>\n" +
                "  <DeviceID>" + clientFromDevice.getUserId() + "</DeviceID>\n" +
                "</Query>";

        System.out.println("=== 开始设备目录查询测试 ===");

        String resultCallId = ClientCommandSender.sendCommand("MESSAGE", clientFromDevice, clientToDevice, catalogContent);

        System.out.println("目录查询CallId: " + resultCallId);

        Assertions.assertNotNull(resultCallId, "目录查询请求已发送");
    }

    @Test
    @Order(5)
    @DisplayName("测试设备配置验证")
    public void testDeviceConfiguration() {
        System.out.println("=== 设备配置验证测试 ===");

        // 获取设备
        FromDevice clientFromDevice = testDeviceSupplier.getClientFromDevice();
        ToDevice clientToDevice = testDeviceSupplier.getClientToDevice();

        // 验证客户端设备配置
        Assertions.assertNotNull(clientFromDevice.getUserId(), "客户端设备ID不能为空");
        Assertions.assertNotNull(clientFromDevice.getIp(), "客户端设备IP不能为空");
        Assertions.assertTrue(clientFromDevice.getPort() > 0, "客户端设备端口必须大于0");

        // 验证服务端设备配置
        Assertions.assertNotNull(clientToDevice.getUserId(), "服务端设备ID不能为空");
        Assertions.assertNotNull(clientToDevice.getIp(), "服务端设备IP不能为空");
        Assertions.assertTrue(clientToDevice.getPort() > 0, "服务端设备端口必须大于0");

        System.out.println("客户端设备配置: " + clientFromDevice.getUserId() + "@" + clientFromDevice.getIp() + ":" + clientFromDevice.getPort());
        System.out.println("服务端设备配置: " + clientToDevice.getUserId() + "@" + clientToDevice.getIp() + ":" + clientToDevice.getPort());

        // 验证设备类型
        Assertions.assertTrue(clientFromDevice instanceof FromDevice, "客户端设备应该是FromDevice类型");
        Assertions.assertTrue(clientToDevice instanceof ToDevice, "服务端设备应该是ToDevice类型");
    }

    @AfterEach
    public void tearDown() {
        // 清理资源
        if (responseLatch != null) {
            responseLatch.countDown();
        }
    }
}