// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_HPP
#define OCPP_HPP

//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//

#include "ld-ev.hpp"

// headers for provided interface implementations
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
#include <generated/interfaces/auth_token_validator/Implementation.hpp>
#include <generated/interfaces/ocpp/Implementation.hpp>
#include <generated/interfaces/ocpp_1_6_charge_point/Implementation.hpp>
#include <generated/interfaces/ocpp_data_transfer/Implementation.hpp>
#include <generated/interfaces/session_cost/Implementation.hpp>

// headers for required interface implementations
#include <generated/interfaces/auth/Interface.hpp>
#include <generated/interfaces/charger_information/Interface.hpp>
#include <generated/interfaces/display_message/Interface.hpp>
#include <generated/interfaces/evse_manager/Interface.hpp>
#include <generated/interfaces/evse_security/Interface.hpp>
#include <generated/interfaces/external_energy_limits/Interface.hpp>
#include <generated/interfaces/iso15118_extensions/Interface.hpp>
#include <generated/interfaces/ocpp_data_transfer/Interface.hpp>
#include <generated/interfaces/reservation/Interface.hpp>
#include <generated/interfaces/system/Interface.hpp>

// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
#include <chrono>
#include <date/date.h>
#include <date/tz.h>
#include <everest/timer.hpp>
#include <everest/util/async/monitor.hpp>
#include <filesystem>
#include <memory>
#include <mutex>
#include <queue>

#include <ocpp/common/types.hpp>
#include <ocpp/v16/charge_point.hpp>
#include <ocpp/v16/charge_point_configuration.hpp>
#include <ocpp/v16/types.hpp>
#include <ocpp/v2/ocpp_types.hpp>

struct ErrorRaised : public Everest::error::Error {};
struct ErrorCleared : public Everest::error::Error {};
struct PowermeterPublicKey {
    std::string value;
};

using EventData = std::variant<types::evse_manager::SessionEvent, ErrorRaised, ErrorCleared, types::system::LogStatus,
                               types::system::FirmwareUpdateStatus, PowermeterPublicKey>;

struct Event {
    int32_t evse_id;
    EventData data;

    Event(int32_t evse_id_, EventData data_) : evse_id(evse_id_), data(std::move(data_)) {
    }
};

using EvseConnectorMap = std::map<int32_t, std::map<int32_t, int32_t>>;
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1

namespace module {

struct Conf {
    std::string ChargePointConfigPath;
    std::string UserConfigPath;
    std::string DatabasePath;
    bool EnableExternalWebsocketControl;
    int PublishChargingScheduleIntervalS;
    int PublishChargingScheduleDurationS;
    std::string MessageLogPath;
    int MessageQueueResumeDelay;
    std::string RequestCompositeScheduleUnit;
    int DelayOcppStart;
    int ResetStopDelay;
};

class OCPP : public Everest::ModuleBase {
public:
    OCPP() = delete;
    OCPP(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider,
         std::unique_ptr<ocpp_1_6_charge_pointImplBase> p_main,
         std::unique_ptr<auth_token_validatorImplBase> p_auth_validator,
         std::unique_ptr<auth_token_providerImplBase> p_auth_provider,
         std::unique_ptr<ocpp_data_transferImplBase> p_data_transfer, std::unique_ptr<ocppImplBase> p_ocpp_generic,
         std::unique_ptr<session_costImplBase> p_session_cost,
         std::vector<std::unique_ptr<charger_informationIntf>> r_charger_information,
         std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager,
         std::vector<std::unique_ptr<external_energy_limitsIntf>> r_evse_energy_sink,
         std::unique_ptr<reservationIntf> r_reservation, std::unique_ptr<authIntf> r_auth,
         std::unique_ptr<systemIntf> r_system, std::unique_ptr<evse_securityIntf> r_security,
         std::vector<std::unique_ptr<ocpp_data_transferIntf>> r_data_transfer,
         std::vector<std::unique_ptr<display_messageIntf>> r_display_message,
         std::vector<std::unique_ptr<iso15118_extensionsIntf>> r_extensions_15118, Conf& config) :
        ModuleBase(info),
        mqtt(mqtt_provider),
        p_main(std::move(p_main)),
        p_auth_validator(std::move(p_auth_validator)),
        p_auth_provider(std::move(p_auth_provider)),
        p_data_transfer(std::move(p_data_transfer)),
        p_ocpp_generic(std::move(p_ocpp_generic)),
        p_session_cost(std::move(p_session_cost)),
        r_charger_information(std::move(r_charger_information)),
        r_evse_manager(std::move(r_evse_manager)),
        r_evse_energy_sink(std::move(r_evse_energy_sink)),
        r_reservation(std::move(r_reservation)),
        r_auth(std::move(r_auth)),
        r_system(std::move(r_system)),
        r_security(std::move(r_security)),
        r_data_transfer(std::move(r_data_transfer)),
        r_display_message(std::move(r_display_message)),
        r_extensions_15118(std::move(r_extensions_15118)),
        config(config){};

    Everest::MqttProvider& mqtt;
    const std::unique_ptr<ocpp_1_6_charge_pointImplBase> p_main;
    const std::unique_ptr<auth_token_validatorImplBase> p_auth_validator;
    const std::unique_ptr<auth_token_providerImplBase> p_auth_provider;
    const std::unique_ptr<ocpp_data_transferImplBase> p_data_transfer;
    const std::unique_ptr<ocppImplBase> p_ocpp_generic;
    const std::unique_ptr<session_costImplBase> p_session_cost;
    const std::vector<std::unique_ptr<charger_informationIntf>> r_charger_information;
    const std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager;
    const std::vector<std::unique_ptr<external_energy_limitsIntf>> r_evse_energy_sink;
    const std::unique_ptr<reservationIntf> r_reservation;
    const std::unique_ptr<authIntf> r_auth;
    const std::unique_ptr<systemIntf> r_system;
    const std::unique_ptr<evse_securityIntf> r_security;
    const std::vector<std::unique_ptr<ocpp_data_transferIntf>> r_data_transfer;
    const std::vector<std::unique_ptr<display_messageIntf>> r_display_message;
    const std::vector<std::unique_ptr<iso15118_extensionsIntf>> r_extensions_15118;
    const Conf& config;

    // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
    // insert your public definitions here
    std::unique_ptr<ocpp::v16::ChargePoint> charge_point;
    std::unique_ptr<ocpp::v16::ChargePointConfiguration> charge_point_config;
    std::unique_ptr<Everest::SteadyTimer> charging_schedules_timer;
    bool ocpp_stopped = false;

    // Return the OCPP connector id from a pair of EVerest EVSE id and connector
    // id
    int32_t get_ocpp_connector_id(int32_t evse_id, int32_t connector_id);
    // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1

protected:
    // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
    // insert your protected definitions here
    // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1

private:
    friend class LdEverest;
    void init();
    void ready();

    // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
    // insert your private definitions here
    std::filesystem::path ocpp_share_path;
    ocpp::v16::ChargingRateUnit composite_schedule_charging_rate_unit;
    void set_external_limits(const std::map<int32_t, ocpp::v16::EnhancedChargingSchedule>& charging_schedules);
    void publish_charging_schedules(const std::map<int32_t, ocpp::v16::EnhancedChargingSchedule>& charging_schedules);

    void init_evse_subscriptions(); // initialize subscriptions to all EVSEs
                                    // provided by r_evse_manager
    void init_evse_connector_map();
    void init_evse_maps();
    void init_module_configuration();
    void handle_config_key(const ocpp::v16::KeyValue& kv);
    EvseConnectorMap evse_connector_map;                 // provides access to OCPP connector id by using
                                                         // EVerests evse and connector id
    std::map<int32_t, int32_t> connector_evse_index_map; // provides access to r_evse_manager index by
                                                         // using OCPP connector id
    everest::lib::util::monitor<std::map<int32_t, bool>> evse_ready_map;
    everest::lib::util::monitor<std::map<int32_t, std::optional<float>>> evse_soc_map;
    std::set<std::string> resuming_session_ids;

    std::mutex event_mutex;
    bool started{false};
    std::queue<Event> event_queue;
    void process_session_event(int32_t evse_id, const types::evse_manager::SessionEvent& session_event);

    std::string source_ext_limit;
    // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};

// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1

} // namespace module

#endif // OCPP_HPP
