#include <QtTest/QtTest>
#include <QTcpServer>
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <QSignalSpy>

#include "service/tcpserver.h"
#include "service/tcpclient.h"
#include "service/server.h"
#include "service/service.h"
#include "items/item.h"
#include "sensors/sensor.h"

class TestTcp : public QObject
{
    Q_OBJECT

private slots:
    void initTestCase()
    {
        // Setup for all tests
    }

    void testTcpServerCreation()
    {
        TcpServer server;
        
        // Server should be created successfully
        QVERIFY(true);
    }

    void testTcpServerLaunch()
    {
        TcpServer server;
        
        // Launch server on any address, port 0 means dynamic port
        bool result = server.launch(QHostAddress::Any, 0);
        
        QVERIFY(result);
        
        // Server should be listening after launch
        QVERIFY(server.isListening());
    }

    void testTcpServerLaunchSpecificPort()
    {
        TcpServer server;
        
        // Try to launch on a specific port
        bool result = server.launch(QHostAddress::LocalHost, 0);
        
        QVERIFY(result);
        
        // Server should be listening
        QVERIFY(server.isListening());
    }

    void testTcpClientCreation()
    {
        TcpClient client;
        
        // Client should be created successfully
        QVERIFY(true);
    }

    void testTcpProtocolMessageFormat()
    {
        // Test the protocol message format: "MSG JSON LEN <size>\n<json>"
        QJsonObject json;
        json["MessageType"] = "TestMessage";
        json["Data"] = QJsonArray();
        
        QByteArray jsonData = QJsonDocument(json).toJson();
        QString message = QString("MSG JSON LEN %1\n").arg(jsonData.size()) + QString::fromUtf8(jsonData);
        
        // Verify message format
        QVERIFY(message.startsWith("MSG JSON LEN "));
        QVERIFY(message.contains("\n"));
        
        // Parse the message size
        QStringList parts = message.split("\n");
        QVERIFY(parts.size() >= 1);
        
        QString sizeStr = parts[0].mid(13); // Skip "MSG JSON LEN "
        bool ok;
        quint64 size = sizeStr.toUInt(&ok);
        QVERIFY(ok);
        QVERIFY(size == jsonData.size());
    }

    void testTcpProtocolParseCommand()
    {
        // Test parsing a command from the protocol
        QByteArray command = "MSG JSON LEN 123\n";
        
        QVERIFY(command.startsWith("MSG JSON LEN "));
        
        QByteArray sizeStr = command.mid(13);
        sizeStr.chop(1); // Remove newline
        
        bool ok;
        quint64 size = sizeStr.toLongLong(&ok);
        QVERIFY(ok);
        QVERIFY(size == 123);
    }

    void testTcpServerSendJson()
    {
        // Start server
        TcpServer server;
        bool serverResult = server.launch(QHostAddress::LocalHost, 0);
        QVERIFY(serverResult);
        
        // Connect a client to trigger client list
        QTcpSocket clientSocket;
        clientSocket.connectToHost(QHostAddress::LocalHost, server.getServerPort());
        QVERIFY(clientSocket.waitForConnected(1000));
        
        // Give server time to accept connection
        QTest::qWait(200);
        
        // Send JSON from server
        QJsonObject json;
        json["MessageType"] = "TestMessage";
        json["Data"] = QJsonArray();
        
        server.sendJson(json);
        
        // Give more time for data to be sent
        QTest::qWait(200);
        
        // Client should receive data (or at least the connection should work)
        // Note: This may fail due to timing in test environment
        QVERIFY(clientSocket.state() == QTcpSocket::ConnectedState);
        
        // Cleanup
        clientSocket.close();
    }

    void testTcpServerProcessIncomingJson()
    {
        // Start server
        TcpServer server;
        bool serverResult = server.launch(QHostAddress::LocalHost, 0);
        QVERIFY(serverResult);
        
        // Connect client
        QTcpSocket clientSocket;
        clientSocket.connectToHost(QHostAddress::LocalHost, server.getServerPort());
        QVERIFY(clientSocket.waitForConnected(1000));
        
        // Give server time to accept connection
        QTest::qWait(100);
        
        // Send a message that the server can process
        QJsonObject json;
        json["MessageType"] = "GetItems";
        json["Data"] = QJsonArray();
        
        QByteArray jsonData = QJsonDocument(json).toJson();
        QString message = QString("MSG JSON LEN %1\n").arg(jsonData.size()) + QString::fromUtf8(jsonData);
        
        clientSocket.write(message.toUtf8());
        clientSocket.flush();
        
        // Give server time to process
        QTest::qWait(100);
        
        // Cleanup
        clientSocket.close();
    }

    void testTcpServerMultipleClients()
    {
        // Start server
        TcpServer server;
        bool serverResult = server.launch(QHostAddress::LocalHost, 0);
        QVERIFY(serverResult);
        
        // Connect multiple clients
        QTcpSocket client1;
        client1.connectToHost(QHostAddress::LocalHost, server.getServerPort());
        QVERIFY(client1.waitForConnected(1000));
        
        QTcpSocket client2;
        client2.connectToHost(QHostAddress::LocalHost, server.getServerPort());
        QVERIFY(client2.waitForConnected(1000));
        
        // Give server time to accept connections
        QTest::qWait(200);
        
        // Send message to all clients
        QJsonObject json;
        json["MessageType"] = "TestMessage";
        json["Data"] = QJsonArray();
        
        server.sendJson(json);
        
        // Give time for data to be sent
        QTest::qWait(200);
        
        // Both clients should be connected (timing may affect actual data receipt)
        QVERIFY(client1.state() == QTcpSocket::ConnectedState);
        QVERIFY(client2.state() == QTcpSocket::ConnectedState);
        
        // Cleanup
        client1.close();
        client2.close();
    }

    void testItemUpdateMessageFormat()
    {
        // Test creating an item update message format
        Item item(1, "test_item", 1);
        
        QJsonObject itemJson;
        item.store(itemJson);
        
        QJsonArray items;
        items.append(itemJson);
        
        // Manually create the message (since createMessage is protected)
        QJsonObject message;
        message["MessageType"] = "ItemUpdate";
        message["Data"] = items;
        message["FullList"] = false;
        
        QVERIFY(message["MessageType"].toString() == "ItemUpdate");
        QVERIFY(message["Data"].toArray().size() == 1);
        QVERIFY(message["FullList"].toBool() == false);
    }

    void testSensorUpdateMessageFormat()
    {
        // Test creating a sensor update message format
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.0, "temp_sensor");
        
        QJsonObject sensorJson;
        sensor.store(sensorJson);
        
        QJsonArray sensors;
        sensors.append(sensorJson);
        
        // Manually create the message (since createMessage is protected)
        QJsonObject message;
        message["MessageType"] = "SensorUpdate";
        message["Data"] = sensors;
        
        QVERIFY(message["MessageType"].toString() == "SensorUpdate");
        QVERIFY(message["Data"].toArray().size() == 1);
    }

    void testTcpClientSocketState()
    {
        // Test that TcpClient creates a socket
        TcpClient client;
        
        // The client should have a valid socket (internal implementation detail)
        // We can verify the client was created successfully
        QVERIFY(true);
    }

    void testTcpServerClientConnection()
    {
        // Start server
        TcpServer server;
        bool serverResult = server.launch(QHostAddress::LocalHost, 0);
        QVERIFY(serverResult);
        
        // Connect a client
        QTcpSocket clientSocket;
        clientSocket.connectToHost(QHostAddress::LocalHost, server.getServerPort());
        
        bool waitResult = clientSocket.waitForConnected(1000);
        QVERIFY(waitResult);
        
        // Verify connection state
        QVERIFY(clientSocket.state() == QTcpSocket::ConnectedState);
        
        // Cleanup
        clientSocket.close();
    }

    void testTcpServerDisconnection()
    {
        // Start server
        TcpServer server;
        bool serverResult = server.launch(QHostAddress::LocalHost, 0);
        QVERIFY(serverResult);
        
        // Connect a client
        QTcpSocket* client = new QTcpSocket();
        client->connectToHost(QHostAddress::LocalHost, server.getServerPort());
        QVERIFY(client->waitForConnected(1000));
        
        // Give server time to accept connection
        QTest::qWait(100);
        
        // Disconnect client
        client->disconnectFromHost();
        QTest::qWait(100);
        
        // Cleanup
        delete client;
    }

    void testTcpProtocolLargeMessage()
    {
        // Test the protocol with a larger message
        QJsonObject json;
        json["MessageType"] = "TestMessage";
        
        // Create a large data array
        QJsonArray data;
        for (int i = 0; i < 100; i++) {
            data.append(QJsonObject{{"id", i}, {"value", QString("item%1").arg(i)}});
        }
        json["Data"] = data;
        
        QByteArray jsonData = QJsonDocument(json).toJson();
        QString message = QString("MSG JSON LEN %1\n").arg(jsonData.size()) + QString::fromUtf8(jsonData);
        
        // Verify message format
        QVERIFY(message.startsWith("MSG JSON LEN "));
        
        // Parse the message size
        QStringList parts = message.split("\n");
        QString sizeStr = parts[0].mid(13);
        bool ok;
        quint64 size = sizeStr.toUInt(&ok);
        QVERIFY(ok);
        QVERIFY(size == jsonData.size());
    }

    void testAddSensorMessageFormat()
    {
        // Test creating an AddSensor message format
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.0, "temp_sensor");

        QJsonObject sensorJson;
        sensor.store(sensorJson);

        QJsonObject payload;
        payload["Topic"] = "home/temperature";
        payload["Name"] = "Living Room";

        // Manually create the message (since createMessage is protected)
        QJsonObject message;
        message["MessageType"] = "AddSensor";
        message["Data"] = QJsonArray();
        message["Sensor"] = sensorJson;
        message["Backend"] = static_cast<int>(Sensor::BACKEND_MQTT);
        message["Payload"] = payload;

        QVERIFY(message["MessageType"].toString() == "AddSensor");
        QVERIFY(message["Sensor"].toObject()["Name"].toString() == "temp_sensor");
        QVERIFY(message["Backend"].toInt() == Sensor::BACKEND_MQTT);
        QVERIFY(message["Payload"].toObject()["Topic"].toString() == "home/temperature");
    }

    void testSensorBackendTypeEnum()
    {
        // Test that the backend type enum values are correct
        QVERIFY(Sensor::BACKEND_MICROCONTROLLER == 0);
        QVERIFY(Sensor::BACKEND_MQTT == 1);
        QVERIFY(Sensor::BACKEND_SUN == 2);
    }

    void testServiceAddSensor()
    {
        // Test that Service::addSensor can be called without crashing
        // and creates proper JSON message format
        TcpServer server;
        bool result = server.launch(QHostAddress::LocalHost, 0);
        QVERIFY(result);

        // Create a sensor and payload
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.0, "temp_sensor");
        QJsonObject payload;
        payload["Topic"] = "home/temperature";
        payload["Name"] = "Living Room";

        // Call addSensor - this should not crash
        // The actual sending to clients depends on network timing
        server.addSensor(sensor, Sensor::BACKEND_MQTT, payload);

        // Test passed if no crash occurred
        QVERIFY(true);
    }

    void testServiceProcessAddSensorMessage()
    {
        // Test processing an incoming AddSensor message
        // We test the JSON parsing directly without network
        QJsonObject sensorJson;
        sensorJson["SensorType"] = static_cast<int>(Sensor::TYPE_TEMPERATURE);
        sensorJson["Id"] = 1;
        sensorJson["Field"] = 25.0;
        sensorJson["Name"] = "temp_sensor";

        QJsonObject payload;
        payload["Topic"] = "home/temperature";
        payload["Name"] = "Living Room";

        QJsonObject message;
        message["MessageType"] = "AddSensor";
        message["Data"] = QJsonArray();
        message["Sensor"] = sensorJson;
        message["Backend"] = static_cast<int>(Sensor::BACKEND_MQTT);
        message["Payload"] = payload;

        // Test that the message can be parsed correctly
        QJsonDocument doc(message);
        QVERIFY(doc.isObject());

        QJsonObject parsed = doc.object();
        QVERIFY(parsed["MessageType"].toString() == "AddSensor");
        QVERIFY(parsed["Backend"].toInt() == Sensor::BACKEND_MQTT);
        QVERIFY(parsed["Payload"].toObject()["Topic"].toString() == "home/temperature");
        QVERIFY(parsed["Sensor"].toObject()["Name"].toString() == "temp_sensor");
    }

    void cleanupTestCase()
    {
        // Cleanup after all tests
    }
};

QTEST_APPLESS_MAIN(TestTcp)

#include "test_tcp.moc"