// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
#pragma once

#include <QDateTime>
#include "../types/loadaction.h"
#include "../types/loaddescriptor.h"
#include "../types/surpluscontext.h"

/*!
 * \brief Vue runtime minimale exposée par un adaptateur à l'arbitre.
 */
struct LoadTelemetry {
    double    currentPowerW = 0;  //!< Puissance mesurée (W).
    bool      available     = true; //!< Faux si l'appareil nymea est absent ou en erreur.
    QDateTime lastActionAt;         //!< Dernier instant où applyAction() a produit un effet.
};

/*!
 * \brief Interface pure des adaptateurs de charge.
 *
 * Les implémentations concrètes héritent de QObject + ILoadAdapter et déclarent leurs
 * propres signaux (telemetryChanged, descriptorChanged).
 *
 * \invariant Les adaptateurs EXÉCUTENT, ils ne décident pas (AGENTS règle 2).
 * \invariant applyAction() écrête les valeurs selon les limites matérielles réelles
 *   (second filet après l'écrêtage de l'arbitre).
 * \invariant applyAction() avec \c reason vide doit être rejetée silencieusement.
 * \invariant Les méthodes non-applyAction() retournent immédiatement (pas de I/O bloquant).
 * \invariant **Temps = paramètre, jamais l'horloge.** Toute logique temporelle d'un
 *   adaptateur (verrous minOn/minOff, fenêtres, fraîcheur…) utilise EXCLUSIVEMENT le
 *   \c now (= \c ctx.timestamp) reçu en paramètre de \c toLoadContext()/applyAction().
 *   JAMAIS \c QDateTime::currentDateTime(). C'est cette source unique, partagée avec le
 *   scheduler, qui rend impossible toute divergence décision/exécution et qui rend la
 *   logique injectable en simulation. Contrat pour tout futur adaptateur (SgReady, Battery).
 */
class ILoadAdapter {
public:
    virtual ~ILoadAdapter() = default;

    /*!
     * \brief Description statique de la charge : capacités, limites, priorité, needs.
     * \return LoadDescriptor construit depuis la configuration matérielle.
     * \note Peut être rappelé à chaque cycle — l'implémentation doit être légère.
     */
    virtual LoadDescriptor descriptor() const = 0;

    /*!
     * \brief Télémétrie runtime (puissance, disponibilité, dernière action).
     * \return LoadTelemetry issue de l'état courant de l'appareil nymea.
     */
    virtual LoadTelemetry  telemetry()    const = 0;

    /*!
     * \brief Construit l'entrée §5 loads[] pour le SurplusContext.
     * \param now Temps de cycle (\c ctx.timestamp). Source unique pour l'évaluation des
     *   verrous (minStage/maxStage) — JAMAIS \c QDateTime::currentDateTime() côté adaptateur,
     *   afin que décision (scheduler) et exécution (applyAction) partagent le même temps.
     * \return LoadContext incluant declared, limits, needs et télémétrie type-spécifique.
     */
    virtual LoadContext    toLoadContext(const QDateTime &now) const = 0;

    /*!
     * \brief Applique l'action et retourne ce qui a réellement été envoyé au matériel.
     *
     * L'arbitre a déjà écrêté selon les limites et le budget — ceci est le second filet.
     *
     * \param action Action à appliquer. Doit avoir \c reason non vide.
     * \param now    Temps de cycle (\c ctx.timestamp) — MÊME source que toLoadContext(),
     *   pour que l'évaluation des verrous coïncide avec celle vue par le scheduler.
     * \return L'action après écrêtage matériel (peut différer de l'entrée).
     * \note Retour silencieux sans effet si \c action.reason est vide.
     */
    virtual LoadAction     applyAction(const LoadAction &action, const QDateTime &now) = 0;
};
