#include <QtTest/QtTest>
#include "sensors/sensor.h"

class TestSensor : public QObject
{
    Q_OBJECT

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

    void testSensorCreation()
    {
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 0.0, "test_sensor");
        
        QCOMPARE(sensor.type, Sensor::TYPE_TEMPERATURE);
        QCOMPARE(sensor.name, QString("test_sensor"));
    }

    void testSensorData()
    {
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 0.0, "temp_sensor");
        
        // Test setting and getting values
        sensor.field = 25.0;
        QCOMPARE(sensor.field, 25.0);
    }

    void testSensorTypes()
    {
        // Test different sensor types
        Sensor doorSensor(Sensor::TYPE_DOOR, 1);
        Sensor tempSensor(Sensor::TYPE_TEMPERATURE, 2);
        Sensor humiditySensor(Sensor::TYPE_HUMIDITY, 3);
        
        QCOMPARE(doorSensor.type, Sensor::TYPE_DOOR);
        QCOMPARE(tempSensor.type, Sensor::TYPE_TEMPERATURE);
        QCOMPARE(humiditySensor.type, Sensor::TYPE_HUMIDITY);
    }

    void testSensorStringParsing()
    {
        // Test parsing sensor from string (format: "SENSOR TYPE: X ID: Y FIELD: Z TIME: T")
        // This tests the sensorFromString function
        QString sensorStr = "SENSOR TYPE: 1 ID: 5 FIELD: 250 TIME: 1234567890";
        Sensor sensor = Sensor::sensorFromString(sensorStr);
        
        // The function should parse the type, id, and field
        // Note: temperature and humidity values are divided by 10
        QVERIFY(sensor.type != Sensor::TYPE_DUMMY || sensor.field != 0);
    }

    void testSensorStringParsingInvalid()
    {
        // Test parsing invalid string returns dummy sensor
        QString invalidStr = "invalid data";
        Sensor sensor = Sensor::sensorFromString(invalidStr);
        
        // Should return a dummy sensor with hidden=true
        QVERIFY(sensor.hidden);
    }

    void testSensorToString()
    {
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.5, "test_sensor");
        QString str = sensor.toString();
        
        // Should contain type, id, and field info
        QVERIFY(str.contains("SENSOR TYPE"));
        QVERIFY(str.contains("ID:"));
        QVERIFY(str.contains("FIELD:"));
    }

    void testSensorJsonSerialization()
    {
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.0, "test_sensor");
        
        QJsonObject json;
        sensor.store(json);
        
        // Verify JSON contents
        QVERIFY(json.contains("Type"));
        QVERIFY(json.contains("SensorType"));
        QVERIFY(json.contains("Id"));
        QVERIFY(json.contains("Field"));
        QVERIFY(json.contains("Name"));
        QVERIFY(json.contains("Unit"));
        
        // Check values
        QVERIFY(json["Type"].toString() == "Sensor");
        QVERIFY(json["SensorType"].toInt() == Sensor::TYPE_TEMPERATURE);
        QVERIFY(json["Id"].toInt() == 1);
        QVERIFY(json["Field"].toDouble() == 25.0);
        QVERIFY(json["Name"].toString() == "test_sensor");
    }

    void testSensorJsonDeserialization()
    {
        QJsonObject json;
        json["SensorType"] = Sensor::TYPE_HUMIDITY;
        json["Id"] = 5;
        json["Field"] = 60.5;
        json["Name"] = "humidity_sensor";
        json["Hidden"] = false;
        
        Sensor sensor(json);
        
        QVERIFY(sensor.type == Sensor::TYPE_HUMIDITY);
        QVERIFY(sensor.id == 5);
        QVERIFY(sensor.field == 60.5);
        QVERIFY(sensor.name == "humidity_sensor");
        QVERIFY(!sensor.hidden);
    }

    void testSensorEquality()
    {
        Sensor sensor1(Sensor::TYPE_TEMPERATURE, 1, 25.0);
        Sensor sensor2(Sensor::TYPE_TEMPERATURE, 1, 30.0);
        Sensor sensor3(Sensor::TYPE_TEMPERATURE, 2, 25.0);
        
        // Same type and id means equal
        QVERIFY(sensor1 == sensor2);
        
        // Different id means not equal
        QVERIFY(sensor1 != sensor3);
    }

    void testSensorUpdateSeen()
    {
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.0);
        QDateTime before = sensor.lastSeen;
        
        // Wait a tiny bit and update
        sensor.updateSeen();
        
        QVERIFY(sensor.lastSeen >= before);
    }

    void testSensorGenerateName()
    {
        // Test auto-generated names for different sensor types
        Sensor tempSensor(Sensor::TYPE_TEMPERATURE, 1);
        QVERIFY(tempSensor.name.contains("Temperature"));
        
        Sensor doorSensor(Sensor::TYPE_DOOR, 2);
        QVERIFY(doorSensor.name.contains("Door"));
        
        Sensor buttonSensor(Sensor::TYPE_BUTTON, 3);
        QVERIFY(buttonSensor.name.contains("Button"));
        
        Sensor humiditySensor(Sensor::TYPE_HUMIDITY, 4);
        QVERIFY(humiditySensor.name.contains("Humidity"));
    }

    void testSensorGetUnit()
    {
        Sensor tempSensor(Sensor::TYPE_TEMPERATURE, 1);
        QVERIFY(tempSensor.getUnit() == "°C");
        
        Sensor humiditySensor(Sensor::TYPE_HUMIDITY, 1);
        QVERIFY(humiditySensor.getUnit() == "%");
        
        Sensor pressureSensor(Sensor::TYPE_PRESSURE, 1);
        QVERIFY(pressureSensor.getUnit() == "hPa");
        
        Sensor brightnessSensor(Sensor::TYPE_BRIGHTNESS, 1);
        QVERIFY(brightnessSensor.getUnit() == "lx");
        
        Sensor co2Sensor(Sensor::TYPE_CO2, 1);
        QVERIFY(co2Sensor.getUnit() == "ppm");
        
        Sensor vocSensor(Sensor::TYPE_TOTAL_VOC, 1);
        QVERIFY(vocSensor.getUnit() == "ppb");
    }

    void testSensorHidden()
    {
        Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.0, "test", false);
        QVERIFY(!sensor.hidden);
        
        Sensor hiddenSensor(Sensor::TYPE_TEMPERATURE, 2, 25.0, "test", true);
        QVERIFY(hiddenSensor.hidden);
    }

    void testSensorAllTypes()
    {
        // Test all sensor types can be created
        Sensor door(Sensor::TYPE_DOOR, 1);
        Sensor temp(Sensor::TYPE_TEMPERATURE, 1);
        Sensor humidity(Sensor::TYPE_HUMIDITY, 1);
        Sensor pressure(Sensor::TYPE_PRESSURE, 1);
        Sensor brightness(Sensor::TYPE_BRIGHTNESS, 1);
        Sensor button(Sensor::TYPE_BUTTON, 1);
        Sensor adc(Sensor::TYPE_ADC, 1);
        Sensor co2(Sensor::TYPE_CO2, 1);
        Sensor pm25(Sensor::TYPE_PM25, 1);
        Sensor voc(Sensor::TYPE_TOTAL_VOC, 1);
        Sensor lowBattery(Sensor::TYPE_LOWBATTERY, 1);
        Sensor occupancy(Sensor::TYPE_OCUPANCY, 1);
        Sensor sunAlt(Sensor::TYPE_SUN_ALTITUDE, 1);
        Sensor audio(Sensor::TYPE_AUDIO_OUTPUT, 1);
        
        // All should be valid
        QVERIFY(door.type == Sensor::TYPE_DOOR);
        QVERIFY(temp.type == Sensor::TYPE_TEMPERATURE);
        QVERIFY(humidity.type == Sensor::TYPE_HUMIDITY);
        QVERIFY(pressure.type == Sensor::TYPE_PRESSURE);
        QVERIFY(brightness.type == Sensor::TYPE_BRIGHTNESS);
        QVERIFY(button.type == Sensor::TYPE_BUTTON);
        QVERIFY(adc.type == Sensor::TYPE_ADC);
        QVERIFY(co2.type == Sensor::TYPE_CO2);
        QVERIFY(pm25.type == Sensor::TYPE_PM25);
        QVERIFY(voc.type == Sensor::TYPE_TOTAL_VOC);
        QVERIFY(lowBattery.type == Sensor::TYPE_LOWBATTERY);
        QVERIFY(occupancy.type == Sensor::TYPE_OCUPANCY);
        QVERIFY(sunAlt.type == Sensor::TYPE_SUN_ALTITUDE);
        QVERIFY(audio.type == Sensor::TYPE_AUDIO_OUTPUT);
    }

    void testSensorStoreUserUpdateUpdatesNameAndHidden()
    {
        // Create a SensorStore
        SensorStore store;
        store.getSensors()->clear();
        
        // Add initial sensor
        Sensor initialSensor(Sensor::TYPE_TEMPERATURE, 1, 20.0, "Initial Name", false);
        store.sensorGotState(initialSensor, SENSOR_UPDATE_BACKEND);
        
        // Verify initial state
        std::vector<Sensor>* sensors = store.getSensors();
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "Initial Name");
        QVERIFY(sensors->at(0).hidden == false);
        
        // Send USER update with new name and hidden state
        Sensor userUpdate(Sensor::TYPE_TEMPERATURE, 1, 25.0, "New Name", true);
        store.sensorGotState(userUpdate, SENSOR_UPDATE_USER);

        // Verify name and hidden were updated, but field was NOT updated
        // (USER updates only update name/hidden/groupName, not field)
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "New Name");
        QVERIFY(sensors->at(0).hidden == true);
        QVERIFY(sensors->at(0).field == 20.0);  // Field unchanged from initial
    }

    void testSensorStoreNonUserUpdateIgnoresNameAndHidden()
    {
        // Create a SensorStore
        SensorStore store;
        store.getSensors()->clear();
        
        // Add initial sensor
        Sensor initialSensor(Sensor::TYPE_TEMPERATURE, 1, 20.0, "Initial Name", false);
        store.sensorGotState(initialSensor, SENSOR_UPDATE_BACKEND);
        
        // Verify initial state
        std::vector<Sensor>* sensors = store.getSensors();
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "Initial Name");
        QVERIFY(sensors->at(0).hidden == false);
        
        // Send BACKEND update with new name and hidden state
        Sensor backendUpdate(Sensor::TYPE_TEMPERATURE, 1, 25.0, "Backend Name", true);
        store.sensorGotState(backendUpdate, SENSOR_UPDATE_BACKEND);
        
        // Verify name and hidden were NOT updated
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "Initial Name");
        QVERIFY(sensors->at(0).hidden == false);
        QVERIFY(sensors->at(0).field == 25.0);
    }

    void testSensorStoreUserUpdateUpdatesKnownSensors()
    {
        // Create a SensorStore
        SensorStore store;
        store.getSensors()->clear();
        
        // Add initial sensor
        Sensor initialSensor(Sensor::TYPE_TEMPERATURE, 1, 20.0, "Initial Name", false);
        store.sensorGotState(initialSensor, SENSOR_UPDATE_BACKEND);
        
        // Send USER update with new name and hidden state
        Sensor userUpdate(Sensor::TYPE_TEMPERATURE, 1, 25.0, "New Name", true);
        store.sensorGotState(userUpdate, SENSOR_UPDATE_USER);
        
        // Store to JSON and reload
        QJsonObject json;
        store.store(json);
        
        SensorStore store2;
        store2.getSensors()->clear();
        store2.load(json);
        
        // Add the sensor again - should use the updated name from knownSensors_
        Sensor newSensor(Sensor::TYPE_TEMPERATURE, 1, 30.0, "Original Name", false);
        store2.sensorGotState(newSensor, SENSOR_UPDATE_BACKEND);
        
        // Verify the name was taken from knownSensors_
        std::vector<Sensor>* sensors = store2.getSensors();
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "New Name");
        QVERIFY(sensors->at(0).hidden == true);
    }

    void testSensorStoreNewSensorNotInKnownSensors()
    {
        // Create a SensorStore
        SensorStore store;
        store.getSensors()->clear();
        
        // Add a new sensor (not in knownSensors_)
        Sensor newSensor(Sensor::TYPE_TEMPERATURE, 99, 25.0, "New Sensor Name", true);
        store.sensorGotState(newSensor, SENSOR_UPDATE_BACKEND);
        
        // Verify sensor was added with its original name
        std::vector<Sensor>* sensors = store.getSensors();
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "New Sensor Name");
        QVERIFY(sensors->at(0).hidden == true);
    }

    void testSensorStoreNewSensorInKnownSensors()
    {
        // Create a SensorStore
        SensorStore store;
        store.getSensors()->clear();
        
        // Load known sensors
        QJsonObject json;
        QJsonArray sensorsArray;
        QJsonObject knownSensor;
        knownSensor["SensorType"] = Sensor::TYPE_TEMPERATURE;
        knownSensor["Id"] = 99;
        knownSensor["Name"] = "Known Sensor Name";
        knownSensor["Hidden"] = true;
        sensorsArray.append(knownSensor);
        json["Sensors"] = sensorsArray;
        store.load(json);
        
        // Add a new sensor that matches knownSensors_
        Sensor newSensor(Sensor::TYPE_TEMPERATURE, 99, 25.0, "Original Name", false);
        store.sensorGotState(newSensor, SENSOR_UPDATE_BACKEND);
        
        // Verify name was overridden from knownSensors_
        std::vector<Sensor>* sensors = store.getSensors();
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "Known Sensor Name");
        QVERIFY(sensors->at(0).hidden == true);
    }

    void testSensorStoreRemoteUpdateIgnored()
    {
        // Create a SensorStore
        SensorStore store;
        store.getSensors()->clear();
        
        // Add initial sensor
        Sensor initialSensor(Sensor::TYPE_TEMPERATURE, 1, 20.0, "Initial Name", false);
        store.sensorGotState(initialSensor, SENSOR_UPDATE_BACKEND);

        // Send REMOTE update with new name and hidden state (e.g., from secondary instance)
        Sensor remoteUpdate(Sensor::TYPE_TEMPERATURE, 1, 25.0, "Remote Name", true);
        store.sensorGotState(remoteUpdate, SENSOR_UPDATE_REMOTE);

        // Verify name, hidden and field were updated (remote updates should sync user-configurable fields)
        std::vector<Sensor>* sensors = store.getSensors();
        QVERIFY(sensors->size() == 1);
        QVERIFY(sensors->at(0).name == "Remote Name");
        QVERIFY(sensors->at(0).hidden == true);
        QVERIFY(sensors->at(0).field == 25.0);
    }

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

QTEST_APPLESS_MAIN(TestSensor)

#include "test_sensor.moc"
