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

#include <QObject>
#include "ischeduler.h"

class EvCharger;
class ChargingAction;
class EnergyArbitrator;

/*!
 * \brief Planificateur basé sur les règles GPL (EV surplus + aWATTar).
 *
 * **Rôle en beta (jusqu'à 3g)** : PROXY AMONT POUR L'EV — délègue toute la
 * planification EV à \c planSurplusCharging() et \c planSpotMarketCharging()
 * héritées de SmartChargingManager. Ces méthodes décident (acquisitionTolerance,
 * batteryLevelConsideration, waterfall, spot market) ; ce scheduler relit leurs
 * sorties (m_chargingActions) et les reformate en LoadAction annotées d'un
 * \c reason français — pour le log [Arbitre] uniquement.
 *
 * À partir de **3g** : la logique de surplus sera transplantée ici (waterfall
 * unifié sur toutes les charges, EV inclus). Voir AGENTS.md § "3b révisé".
 *
 * \invariant getPlan() retourne IMMÉDIATEMENT (AGENTS invariant 5).
 * \invariant getPlan() retourne toujours un Plan valide (isValid() == true).
 * \invariant Toute LoadAction a un \c reason non vide, en français.
 * \invariant Priorité : Deadline VE > Surplus PV > aWATTar > Min courant > Idle.
 *   Identique à adjustEvChargers() amont — iso-fonctionnel 3b.
 */
class RuleBasedScheduler : public QObject, public IScheduler
{
    Q_OBJECT
public:
    /*!
     * \brief Constructeur.
     * \param arbitrator Arbitre propriétaire — fournit l'accès à la planification et à l'état.
     * \param parent     Propriétaire Qt.
     */
    explicit RuleBasedScheduler(EnergyArbitrator *arbitrator, QObject *parent = nullptr);

    /*!
     * \brief Retourne le plan pour le slot courant.
     *
     * **Beta (proxy)** : appelle \c runSpotMarketPlanning() puis \c runSurplusPlanning()
     * (qui écrivent dans m_chargingActions via les méthodes parent), lit le résultat
     * via \c scheduledActions(), et construit les LoadAction correspondantes.
     * Les LoadAction servent uniquement au log [Arbitre] — le dispatch réel reste dans
     * \c adjustEvChargers() amont.
     *
     * \param ctx SurplusContext courant. En 3b : seul \c ctx.timestamp est utilisé.
     * \return Plan à 1 créneau couvrant \c ctx.timestamp + 60 s.
     */
    Plan getPlan(const SurplusContext &ctx) override;

private:
    /*!
     * \brief Construit un LoadAction pour le cas "délai requis" (TimeRequirement).
     * \param ev EvCharger concerné.
     * \param ca ChargingAction planifiée (courant et phases déjà calculés par planSurplusCharging).
     * \return LoadAction avec funding=Grid et reason "Deadline VE".
     */
    LoadAction buildTimeRequirementAction(EvCharger *ev, const ChargingAction &ca) const;

    /*!
     * \brief Construit un LoadAction "courant minimum" pour les modes EcoMin.
     * \param ev EvCharger concerné.
     * \return LoadAction avec funding=Surplus, chargingEnabled=true, currentA=min.
     */
    LoadAction buildMinCurrentAction(EvCharger *ev) const;

    /*!
     * \brief Construit un LoadAction "idle" (recharge désactivée, aucun surplus).
     * \param ev EvCharger concerné.
     * \return LoadAction avec chargingEnabled=false et reason appropriée.
     */
    LoadAction buildIdleAction(EvCharger *ev) const;

    /*!
     * \brief Construit un LoadAction "stage" ECS par cascade de surplus (waterfall §6).
     *
     * Retient le palier déclaré le plus haut dont la puissance tient dans le budget courant.
     * Correction B (anti-clignotement) : recrédite d'abord \c lc.telemetry.currentPowerW au
     * budget (la conso actuelle de l'ECS est déjà soustraite de l'export mesuré), puis
     * décrémente \c remainingSurplusW de la puissance du palier retenu.
     *
     * \param lc                Charge ECS (adapter == "relay-stages") du SurplusContext.
     * \param[in,out] remainingSurplusW  Budget de surplus restant (W) ; mis à jour pour la
     *                          charge ECS suivante (priorité inférieure / rang supérieur).
     * \return LoadAction kind=Stage, funding=Surplus, \c reason français non vide.
     */
    LoadAction buildEcsStageAction(const LoadContext &lc, double &remainingSurplusW) const;

    EnergyArbitrator *m_arbitrator;
};
