// SPDX-License-Identifier: GPL-3.0-or-later
// Extension : Gestionnaire Pompe à chaleur + ECS — implémentation

#include "heatpumpmanager.h"

#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(dcNymeaEnergy)

// ─────────────────────────────────────────────────────────────
// HeatPumpManager
// ─────────────────────────────────────────────────────────────

HeatPumpManager::HeatPumpManager(EnergyManager *energyManager,
                                 ThingManager *thingManager,
                                 QObject *parent)
    : QObject(parent),
      m_energyManager(energyManager),
      m_thingManager(thingManager)
{
    // Détecter les Things déjà présents au démarrage
    for (Thing *thing : m_thingManager->configuredThings()) {
        onThingAdded(thing);
    }

    // S'abonner aux nouveaux appareils
    connect(m_thingManager, &ThingManager::thingAdded,
            this, &HeatPumpManager::onThingAdded);
    connect(m_thingManager, &ThingManager::thingRemoved,
            this, &HeatPumpManager::onThingRemoved);
}

// ─── Accesseurs ──────────────────────────────────────────────

QList<HeatPumpDevice *> HeatPumpManager::heatPumps() const
{
    return m_heatPumps.values();
}

QList<DomesticHotWaterDevice *> HeatPumpManager::dhwDevices() const
{
    return m_dhwDevices.values();
}

double HeatPumpManager::totalThermalPower() const
{
    double total = 0;
    for (const HeatPumpDevice *hp : m_heatPumps)
        total += hp->currentPower();
    for (const DomesticHotWaterDevice *dhw : m_dhwDevices)
        total += dhw->currentPower();
    return total;
}

// ─── Contrôle PAC ────────────────────────────────────────────

EnergyManager::EnergyError HeatPumpManager::setPower(const ThingId &thingId, bool on)
{
    HeatPumpDevice *hp = m_heatPumps.value(thingId);
    if (!hp) return EnergyManager::EnergyErrorThingNotFound;

    qCDebug(dcNymeaEnergy()) << "HeatPumpManager: setPower" << on
                             << "on" << hp->name();

    Action action(kHPStatePower, thingId);
    action.setParams(ParamList() << Param(kHPStatePower, on));
    m_thingManager->executeAction(action);

    return EnergyManager::EnergyErrorNoError;
}

EnergyManager::EnergyError HeatPumpManager::setMode(const ThingId &thingId,
                                                    const QString &mode)
{
    HeatPumpDevice *hp = m_heatPumps.value(thingId);
    if (!hp) return EnergyManager::EnergyErrorThingNotFound;

    const QStringList validModes = {"heating", "cooling", "auto", "standby"};
    if (!validModes.contains(mode)) return EnergyManager::EnergyErrorInvalidParameter;

    qCDebug(dcNymeaEnergy()) << "HeatPumpManager: setMode" << mode
                             << "on" << hp->name();

    Action action(kHPActionSetMode, thingId);
    action.setParams(ParamList() << Param(kHPActionParamMode, mode));
    m_thingManager->executeAction(action);

    return EnergyManager::EnergyErrorNoError;
}

EnergyManager::EnergyError HeatPumpManager::setTargetTemperature(const ThingId &thingId,
                                                                  double temperature)
{
    HeatPumpDevice *hp = m_heatPumps.value(thingId);
    if (!hp) return EnergyManager::EnergyErrorThingNotFound;

    if (temperature < 15 || temperature > 65)
        return EnergyManager::EnergyErrorInvalidParameter;

    qCDebug(dcNymeaEnergy()) << "HeatPumpManager: setTargetTemperature"
                             << temperature << "°C on" << hp->name();

    Action action(kHPActionSetTargetTemperature, thingId);
    action.setParams(ParamList() << Param(kHPActionParamTargetTemp, temperature));
    m_thingManager->executeAction(action);

    return EnergyManager::EnergyErrorNoError;
}

// ─── Contrôle ECS ────────────────────────────────────────────

EnergyManager::EnergyError HeatPumpManager::setDHWPower(const ThingId &thingId,
                                                         bool on)
{
    DomesticHotWaterDevice *dhw = m_dhwDevices.value(thingId);
    if (!dhw) return EnergyManager::EnergyErrorThingNotFound;

    qCDebug(dcNymeaEnergy()) << "HeatPumpManager: DHW setPower" << on
                             << "on" << dhw->name();

    Action action(kDHWStatePower, thingId);
    action.setParams(ParamList() << Param(kDHWStatePower, on));
    m_thingManager->executeAction(action);

    return EnergyManager::EnergyErrorNoError;
}

EnergyManager::EnergyError HeatPumpManager::setDHWTargetTemperature(const ThingId &thingId,
                                                                      double temperature)
{
    DomesticHotWaterDevice *dhw = m_dhwDevices.value(thingId);
    if (!dhw) return EnergyManager::EnergyErrorThingNotFound;

    if (temperature < 40 || temperature > 75)
        return EnergyManager::EnergyErrorInvalidParameter;

    Action action(kDHWStateTargetTemperature, thingId);
    action.setParams(ParamList() << Param(kDHWStateTargetTemperature, temperature));
    m_thingManager->executeAction(action);

    return EnergyManager::EnergyErrorNoError;
}

EnergyManager::EnergyError HeatPumpManager::triggerDHWBoost(const ThingId &thingId,
                                                              uint durationMinutes)
{
    DomesticHotWaterDevice *dhw = m_dhwDevices.value(thingId);
    if (!dhw) return EnergyManager::EnergyErrorThingNotFound;

    if (durationMinutes < 15 || durationMinutes > 120)
        return EnergyManager::EnergyErrorInvalidParameter;

    qCDebug(dcNymeaEnergy()) << "HeatPumpManager: DHW triggerBoost"
                             << durationMinutes << "min on" << dhw->name();

    Action action(kDHWActionTriggerBoost, thingId);
    action.setParams(ParamList() << Param(kDHWActionParamDuration, durationMinutes));
    m_thingManager->executeAction(action);

    return EnergyManager::EnergyErrorNoError;
}

// ─── Slots privés ────────────────────────────────────────────

void HeatPumpManager::onThingAdded(Thing *thing)
{
    if (thing->thingClassId() == kHeatPumpThingClassId) {
        setupHeatPump(thing);
    } else if (thing->thingClassId() == kDomesticHotWaterClassId) {
        setupDHWDevice(thing);
    }
}

void HeatPumpManager::onThingRemoved(const ThingId &thingId)
{
    if (m_heatPumps.contains(thingId)) {
        qCDebug(dcNymeaEnergy()) << "HeatPumpManager: PAC supprimée:" << thingId;
        delete m_heatPumps.take(thingId);
        emit heatPumpRemoved(thingId);
    }
    if (m_dhwDevices.contains(thingId)) {
        qCDebug(dcNymeaEnergy()) << "HeatPumpManager: ECS supprimée:" << thingId;
        delete m_dhwDevices.take(thingId);
        emit dhwDeviceRemoved(thingId);
    }
}

void HeatPumpManager::onThingStateChanged(Thing *thing,
                                           const StateType &stateType,
                                           const QVariant &value,
                                           const QVariant & /*minValue*/,
                                           const QVariant & /*maxValue*/)
{
    if (m_heatPumps.contains(thing->id())) {
        emit heatPumpStateChanged(thing->id(), stateType.name(), value);
    } else if (m_dhwDevices.contains(thing->id())) {
        emit dhwStateChanged(thing->id(), stateType.name(), value);
    }
}

void HeatPumpManager::setupHeatPump(Thing *thing)
{
    qCDebug(dcNymeaEnergy()) << "HeatPumpManager: Nouvelle PAC:"
                             << thing->name() << thing->id();

    HeatPumpDevice *hp = new HeatPumpDevice(thing, this);
    m_heatPumps.insert(thing->id(), hp);

    // Écouter les changements d'état
    connect(thing, &Thing::stateValueChanged,
            this, [this, thing](const StateTypeId &stateTypeId, const QVariant &value) {
        Thing *t = qobject_cast<Thing *>(sender());
        if (t) emit heatPumpStateChanged(t->id(), stateTypeId.toString(), value);
    });

    emit heatPumpAdded(thing->id());
}

void HeatPumpManager::setupDHWDevice(Thing *thing)
{
    qCDebug(dcNymeaEnergy()) << "HeatPumpManager: Nouvelle ECS:"
                             << thing->name() << thing->id();

    DomesticHotWaterDevice *dhw = new DomesticHotWaterDevice(thing, this);
    m_dhwDevices.insert(thing->id(), dhw);

    connect(thing, &Thing::stateValueChanged,
            this, [this, thing](const StateTypeId &stateTypeId, const QVariant &value) {
        Thing *t = qobject_cast<Thing *>(sender());
        if (t) emit dhwStateChanged(t->id(), stateTypeId.toString(), value);
    });

    emit dhwDeviceAdded(thing->id());
}

// ─────────────────────────────────────────────────────────────
// HeatPumpDevice
// ─────────────────────────────────────────────────────────────

HeatPumpDevice::HeatPumpDevice(Thing *thing, QObject *parent)
    : QObject(parent), m_thing(thing)
{}

ThingId HeatPumpDevice::id() const { return m_thing->id(); }
QString HeatPumpDevice::name() const { return m_thing->name(); }
Thing *HeatPumpDevice::thing() const { return m_thing; }

bool HeatPumpDevice::power() const {
    return m_thing->stateValue(kHPStatePower).toBool();
}
QString HeatPumpDevice::mode() const {
    return m_thing->stateValue(kHPStateMode).toString();
}
double HeatPumpDevice::targetTemperature() const {
    return m_thing->stateValue(kHPStateTargetTemperature).toDouble();
}
double HeatPumpDevice::currentTemperature() const {
    return m_thing->stateValue(kHPStateCurrentTemp).toDouble();
}
double HeatPumpDevice::currentPower() const {
    return m_thing->stateValue(kHPStateCurrentPower).toDouble();
}
double HeatPumpDevice::cop() const {
    return m_thing->stateValue(kHPStateCop).toDouble();
}
bool HeatPumpDevice::defrostActive() const {
    return m_thing->stateValue(kHPStateDefrostActive).toBool();
}

// ─────────────────────────────────────────────────────────────
// DomesticHotWaterDevice
// ─────────────────────────────────────────────────────────────

DomesticHotWaterDevice::DomesticHotWaterDevice(Thing *thing, QObject *parent)
    : QObject(parent), m_thing(thing)
{}

ThingId DomesticHotWaterDevice::id() const { return m_thing->id(); }
QString DomesticHotWaterDevice::name() const { return m_thing->name(); }
Thing *DomesticHotWaterDevice::thing() const { return m_thing; }

bool DomesticHotWaterDevice::power() const {
    return m_thing->stateValue(kDHWStatePower).toBool();
}
double DomesticHotWaterDevice::targetTemperature() const {
    return m_thing->stateValue(kDHWStateTargetTemperature).toDouble();
}
double DomesticHotWaterDevice::currentTemperature() const {
    return m_thing->stateValue(kDHWStateCurrentTemp).toDouble();
}
bool DomesticHotWaterDevice::boostMode() const {
    return m_thing->stateValue(kDHWStateBoostMode).toBool();
}
double DomesticHotWaterDevice::currentPower() const {
    return m_thing->stateValue(kDHWStateCurrentPower).toDouble();
}
