// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef EVSE_MANAGER_HPP
#define EVSE_MANAGER_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/dc_external_derate/Implementation.hpp>
#include <generated/interfaces/energy/Implementation.hpp>
#include <generated/interfaces/evse_manager/Implementation.hpp>
#include <generated/interfaces/uk_random_delay/Implementation.hpp>

// headers for required interface implementations
#include <generated/interfaces/ISO15118_charger/Interface.hpp>
#include <generated/interfaces/ac_rcd/Interface.hpp>
#include <generated/interfaces/connector_lock/Interface.hpp>
#include <generated/interfaces/evse_board_support/Interface.hpp>
#include <generated/interfaces/isolation_monitor/Interface.hpp>
#include <generated/interfaces/kvs/Interface.hpp>
#include <generated/interfaces/over_voltage_monitor/Interface.hpp>
#include <generated/interfaces/power_supply_DC/Interface.hpp>
#include <generated/interfaces/powermeter/Interface.hpp>
#include <generated/interfaces/slac/Interface.hpp>

// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <ctime>
#include <date/date.h>
#include <date/tz.h>
#include <future>
#include <iostream>
#include <optional>

#include "CarManufacturer.hpp"
#include "Charger.hpp"
#include "ErrorHandling.hpp"
#include "PersistentStore.hpp"
#include "SessionLog.hpp"
#include "VarContainer.hpp"
#include "over_voltage/OverVoltageMonitor.hpp"
#include "scoped_lock_timeout.hpp"
#include "voltage_plausibility/VoltagePlausibilityMonitor.hpp"
#include <everest/util/async/monitor.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1

namespace module {

struct Conf {
    int connector_id;
    std::string connector_type;
    std::string evse_id;
    std::string evse_id_din;
    bool payment_enable_eim;
    bool payment_enable_contract;
    double ac_nominal_voltage;
    bool ev_receipt_required;
    bool session_logging;
    std::string session_logging_path;
    bool session_logging_xml;
    bool has_ventilation;
    std::string charge_mode;
    bool supported_iso_ac_bpt;
    bool ac_hlc_enabled;
    bool ac_hlc_use_5percent;
    bool ac_enforce_hlc;
    bool ac_with_soc;
    int internal_over_voltage_duration_ms;
    bool dbg_hlc_auth_after_tstep;
    int dc_isolation_voltage_V;
    int cable_check_wait_number_of_imd_measurements;
    bool cable_check_enable_imd_self_test;
    bool cable_check_enable_imd_self_test_relays_open;
    int cable_check_relays_open_voltage_V;
    int cable_check_relays_closed_timeout_s;
    bool cable_check_wait_below_60V_before_finish;
    bool hack_skoda_enyaq;
    int hack_present_current_offset;
    bool hack_pause_imd_during_precharge;
    bool hack_allow_bpt_with_iso2;
    bool hack_simplified_mode_limit_10A;
    bool autocharge_use_slac_instead_of_hlc;
    bool enable_autocharge;
    std::string logfile_suffix;
    double soft_over_current_tolerance_percent;
    double soft_over_current_measurement_noise_A;
    bool hack_fix_hlc_integer_current_requests;
    bool disable_authentication;
    bool sae_j2847_2_bpt_enabled;
    std::string sae_j2847_2_bpt_mode;
    bool request_zero_power_in_idle;
    bool external_ready_to_start_charging;
    bool uk_smartcharging_random_delay_enable;
    int uk_smartcharging_random_delay_max_duration;
    bool uk_smartcharging_random_delay_at_any_change;
    int initial_meter_value_timeout_ms;
    int switch_3ph1ph_delay_s;
    std::string switch_3ph1ph_cp_state;
    int soft_over_current_timeout_ms;
    bool lock_connector_in_state_b;
    int state_F_after_fault_ms;
    bool fail_on_powermeter_errors;
    bool raise_mrec9;
    int sleep_before_enabling_pwm_hlc_mode_ms;
    bool central_contract_validation_allowed;
    bool contract_certificate_installation_enabled;
    bool inoperative_error_use_vendor_id;
    double voltage_plausibility_max_spread_threshold_V;
    int voltage_plausibility_fault_duration_ms;
    std::string session_id_type;
    bool zero_power_ignore_pause;
    bool zero_power_allow_ev_to_ignore_pause;
    std::string bpt_channel;
    std::string bpt_generator_mode;
    std::string bpt_grid_code_island_method;
    int hlc_charge_loop_without_energy_timeout_s;
    int dc_ramp_ampere_per_second;
};

class EvseManager : public Everest::ModuleBase {
public:
    EvseManager() = delete;
    EvseManager(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider, Everest::TelemetryProvider& telemetry,
                std::unique_ptr<evse_managerImplBase> p_evse, std::unique_ptr<energyImplBase> p_energy_grid,
                std::unique_ptr<auth_token_providerImplBase> p_token_provider,
                std::unique_ptr<uk_random_delayImplBase> p_random_delay,
                std::unique_ptr<dc_external_derateImplBase> p_dc_external_derate,
                std::unique_ptr<evse_board_supportIntf> r_bsp, std::vector<std::unique_ptr<ac_rcdIntf>> r_ac_rcd,
                std::vector<std::unique_ptr<connector_lockIntf>> r_connector_lock,
                std::vector<std::unique_ptr<powermeterIntf>> r_powermeter_grid_side,
                std::vector<std::unique_ptr<powermeterIntf>> r_powermeter_car_side,
                std::vector<std::unique_ptr<slacIntf>> r_slac, std::vector<std::unique_ptr<ISO15118_chargerIntf>> r_hlc,
                std::vector<std::unique_ptr<isolation_monitorIntf>> r_imd,
                std::vector<std::unique_ptr<over_voltage_monitorIntf>> r_over_voltage_monitor,
                std::vector<std::unique_ptr<power_supply_DCIntf>> r_powersupply_DC,
                std::vector<std::unique_ptr<kvsIntf>> r_store, Conf& config) :
        ModuleBase(info),
        mqtt(mqtt_provider),
        telemetry(telemetry),
        p_evse(std::move(p_evse)),
        p_energy_grid(std::move(p_energy_grid)),
        p_token_provider(std::move(p_token_provider)),
        p_random_delay(std::move(p_random_delay)),
        p_dc_external_derate(std::move(p_dc_external_derate)),
        r_bsp(std::move(r_bsp)),
        r_ac_rcd(std::move(r_ac_rcd)),
        r_connector_lock(std::move(r_connector_lock)),
        r_powermeter_grid_side(std::move(r_powermeter_grid_side)),
        r_powermeter_car_side(std::move(r_powermeter_car_side)),
        r_slac(std::move(r_slac)),
        r_hlc(std::move(r_hlc)),
        r_imd(std::move(r_imd)),
        r_over_voltage_monitor(std::move(r_over_voltage_monitor)),
        r_powersupply_DC(std::move(r_powersupply_DC)),
        r_store(std::move(r_store)),
        config(config){};

    Everest::MqttProvider& mqtt;
    Everest::TelemetryProvider& telemetry;
    const std::unique_ptr<evse_managerImplBase> p_evse;
    const std::unique_ptr<energyImplBase> p_energy_grid;
    const std::unique_ptr<auth_token_providerImplBase> p_token_provider;
    const std::unique_ptr<uk_random_delayImplBase> p_random_delay;
    const std::unique_ptr<dc_external_derateImplBase> p_dc_external_derate;
    const std::unique_ptr<evse_board_supportIntf> r_bsp;
    const std::vector<std::unique_ptr<ac_rcdIntf>> r_ac_rcd;
    const std::vector<std::unique_ptr<connector_lockIntf>> r_connector_lock;
    const std::vector<std::unique_ptr<powermeterIntf>> r_powermeter_grid_side;
    const std::vector<std::unique_ptr<powermeterIntf>> r_powermeter_car_side;
    const std::vector<std::unique_ptr<slacIntf>> r_slac;
    const std::vector<std::unique_ptr<ISO15118_chargerIntf>> r_hlc;
    const std::vector<std::unique_ptr<isolation_monitorIntf>> r_imd;
    const std::vector<std::unique_ptr<over_voltage_monitorIntf>> r_over_voltage_monitor;
    const std::vector<std::unique_ptr<power_supply_DCIntf>> r_powersupply_DC;
    const std::vector<std::unique_ptr<kvsIntf>> r_store;
    const Conf& config;

    // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
    // insert your public definitions here
    std::unique_ptr<Charger> charger;
    sigslot::signal<int> signalNrOfPhasesAvailable;
    types::powermeter::Powermeter get_latest_powermeter_data_billing();
    types::evse_board_support::HardwareCapabilities get_hw_capabilities();
    std::atomic<bool> ready_for_capabilities{false};

    std::mutex external_local_limits_mutex;
    bool update_max_current_limit(types::energy::ExternalLimits& limits, float max_current_import,
                                  float max_current_export);
    bool update_max_watt_limit(types::energy::ExternalLimits& limits, float max_watt_export,
                               std::optional<float> max_watt_import);
    void update_to_zero_discharge_limit(types::energy::ExternalLimits& limits);
    bool update_local_energy_limit(types::energy::ExternalLimits l);
    types::energy::ExternalLimits get_local_energy_limits();

    void cancel_reservation(bool signal_event);
    bool is_reserved();

    std::optional<types::evse_manager::ConnectorTypeEnum> connector_type;

    ///
    /// \brief Reserve this evse.
    /// \param id                       The reservation id.
    /// \param signal_reservation_event True when other modules must be signalled about a new reservation (session
    ///                                 event).
    /// \return True on success.
    ///
    bool reserve(int32_t id, const bool signal_reservation_event = true);
    int32_t get_reservation_id();

    bool get_hlc_waiting_for_auth_pnc();
    void set_pnc_enabled(const bool pnc_enabled);
    void set_central_contract_validation_allowed(const bool central_contract_validation_allowed);
    void set_contract_certificate_installation_enabled(const bool contract_certificate_installation_enabled);

    sigslot::signal<types::evse_manager::SessionEvent> signalReservationEvent;

    void charger_was_authorized();

    const std::vector<std::unique_ptr<powermeterIntf>>& r_powermeter_billing();

    // FIXME: this will be removed with proper integration of BPT on ISO-20
    // on DIN SPEC and -2 we claim a positive charging current on ISO protocol,
    // but the power supply switches to discharge if this flag is set.
    std::atomic_bool is_actually_exporting_to_grid{false};

    types::evse_manager::EVInfo get_ev_info();
    void apply_new_target_voltage_current();
    void process_dc_ev_target_voltage_current(const types::iso15118::DcEvseMaximumLimits& hlc_limits);

    std::string selected_protocol = "Unknown";

    std::atomic_bool sae_bidi_active{false};

    void ready_to_start_charging();

    std::unique_ptr<IECStateMachine> bsp;
    std::unique_ptr<ErrorHandling> error_handling;
    std::unique_ptr<PersistentStore> store;

    std::atomic_bool random_delay_enabled{false};
    std::atomic_bool random_delay_running{false};
    std::chrono::time_point<std::chrono::steady_clock> random_delay_end_time;
    std::chrono::time_point<date::utc_clock> random_delay_start_time;
    std::atomic<std::chrono::seconds> random_delay_max_duration;
    std::atomic<std::chrono::time_point<std::chrono::steady_clock>> timepoint_ready_for_charging;

    bool session_is_iso_d20_ac_bpt();
    bool session_is_iso_d20_dc_bpt();

    types::power_supply_DC::Capabilities get_powersupply_capabilities();
    void set_external_derating(types::dc_external_derate::ExternalDerating d);

    void update_powersupply_capabilities(types::power_supply_DC::Capabilities caps) {
        std::scoped_lock lock(powersupply_capabilities_mutex);

        if (caps != powersupply_capabilities) {
            r_hlc[0]->call_set_powersupply_capabilities(caps);
        }

        powersupply_capabilities = caps;

        // Inform HLC layer about update of physical values
        types::iso15118::SetupPhysicalValues setup_physical_values;
        setup_physical_values.dc_current_regulation_tolerance = powersupply_capabilities.current_regulation_tolerance_A;
        setup_physical_values.dc_peak_current_ripple = powersupply_capabilities.peak_current_ripple_A;
        setup_physical_values.dc_energy_to_be_delivered = 10000;
        r_hlc[0]->call_set_charging_parameters(setup_physical_values);

        types::iso15118::DcEvseMinimumLimits evse_min_limits;
        evse_min_limits.evse_minimum_current_limit = powersupply_capabilities.min_export_current_A;
        evse_min_limits.evse_minimum_voltage_limit = powersupply_capabilities.min_export_voltage_V;
        evse_min_limits.evse_minimum_power_limit =
            evse_min_limits.evse_minimum_current_limit * evse_min_limits.evse_minimum_voltage_limit;
        r_hlc[0]->call_update_dc_minimum_limits(evse_min_limits);

        // HLC layer will also get new maximum current/voltage/watt limits etc, but those will need to run through
        // energy management first. Those limits will be applied in energy_grid implementation when requesting
        // energy, so it is enough to set the powersupply_capabilities here.
        // FIXME: this is not implemented yet: enforce_limits uses the enforced limits to tell HLC, but capabilities
        // limits are not yet included in request.
    }
    std::atomic_int ac_nr_phases_active{0};
    // 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::mutex powersupply_capabilities_mutex;
    types::power_supply_DC::Capabilities powersupply_capabilities;
    std::mutex dc_external_derate_mutex;
    types::dc_external_derate::ExternalDerating dc_external_derate;

    Everest::timed_mutex_traceable power_mutex;
    types::powermeter::Powermeter latest_powermeter_data_billing;

    Everest::Thread energyThreadHandle;
    everest::lib::util::monitor<types::evse_board_support::HardwareCapabilities> hw_capabilities;

    types::energy::ExternalLimits external_local_energy_limits;
    const float EVSE_ABSOLUTE_MAX_CURRENT = 80.0;
    bool slac_enabled;

    std::atomic_bool contactor_open{true};

    Everest::timed_mutex_traceable charger_ready_mutex;
    bool charger_ready{false};
    std::atomic_bool hlc_enabled;

    std::atomic_bool hlc_waiting_for_auth_eim;
    std::atomic_bool hlc_waiting_for_auth_pnc;

    std::atomic_bool pnc_enabled{false};
    std::atomic_bool central_contract_validation_allowed{false};
    std::atomic_bool contract_certificate_installation_enabled{false};

    VarContainer<types::isolation_monitor::IsolationMeasurement> isolation_measurement;
    VarContainer<types::power_supply_DC::VoltageCurrent> powersupply_measurement;
    VarContainer<bool> selftest_result;
    std::unique_ptr<OverVoltageMonitor> internal_over_voltage_monitor;

    // Track voltage to earth failures for debouncing
    int voltage_to_earth_failure_count{0};
    std::chrono::steady_clock::time_point first_voltage_to_earth_failure_time{};
    static constexpr std::chrono::seconds MIN_TIME_BETWEEN_FIRST_AND_LAST_FAILURE{2};
    static constexpr int REQUIRED_CONSECUTIVE_FAILURES{2};

    std::atomic<double> latest_target_current_low_pass{0.};
    std::atomic<std::chrono::steady_clock::time_point> latest_target_current_low_pass_last_update{};
    std::atomic<double> latest_target_voltage{0.};
    std::atomic<double> latest_target_current{0.};
    std::atomic<double> last_power_supply_voltage{0.};
    std::atomic<double> last_power_supply_current{0.};

    // Raw EV target values as received from ISO15118 stack
    std::atomic<double> raw_ev_target_voltage{0.};
    std::atomic<double> raw_ev_target_current{0.};

    types::authorization::ProvidedIdToken autocharge_token;

    void log_v2g_message(types::iso15118::V2gMessages const& v2g_messages);

    // Reservations
    bool reserved;
    int32_t reservation_id;
    Everest::timed_mutex_traceable reservation_mutex;

    // Voltage plausibility monitor
    std::unique_ptr<VoltagePlausibilityMonitor> voltage_plausibility_monitor;

    void setup_AC_mode();
    void setup_fake_DC_mode();

    // special funtion to switch mode while session is active
    void switch_AC_mode();
    void switch_DC_mode();

    // DC handlers
    void cable_check();

    void powersupply_DC_on();
    std::atomic_bool powersupply_dc_is_on{false};
    bool powersupply_DC_set(double voltage, double current);
    void powersupply_DC_off();
    bool wait_powersupply_DC_voltage_reached(double target_voltage);
    bool wait_powersupply_DC_below_voltage(double target_voltage);

    bool cable_check_should_exit();

    double get_emergency_over_voltage_threshold();
    double get_error_over_voltage_threshold();

    // EV information
    Everest::timed_mutex_traceable ev_info_mutex;
    types::evse_manager::EVInfo ev_info;
    types::evse_manager::CarManufacturer car_manufacturer{types::evse_manager::CarManufacturer::Unknown};

    void imd_stop();
    void imd_start();
    Everest::Thread telemetryThreadHandle;

    void fail_cable_check(const std::string& reason);

    // setup sae j2847/2 v2h mode
    void setup_v2h_mode();

    bool check_isolation_resistance_in_range(double resistance);
    bool check_voltage_to_protective_earth_in_range(types::isolation_monitor::IsolationMeasurement m);

    static constexpr double CABLECHECK_CURRENT_LIMIT{2};
    static constexpr double CABLECHECK_INSULATION_FAULT_RESISTANCE_OHM{100000.};
    static constexpr double CABLECHECK_SAFE_VOLTAGE{60.};
    static constexpr int CABLECHECK_SELFTEST_TIMEOUT{30};

    std::atomic_bool current_demand_active{false};
    std::atomic_bool slac_unmatched{false};
    std::mutex powermeter_mutex;
    std::condition_variable powermeter_cv;
    bool initial_powermeter_value_received{false};

    std::optional<types::iso15118::ServiceCategory> selected_d20_energy_service{std::nullopt};

    std::atomic<types::power_supply_DC::ChargingPhase> power_supply_DC_charging_phase{
        types::power_supply_DC::ChargingPhase::Other};

    types::power_supply_DC::ChargingPhase last_power_supply_DC_charging_phase{
        types::power_supply_DC::ChargingPhase::Other};
    everest::lib::util::monitor<std::vector<types::iso15118::EnergyTransferMode>> supported_energy_transfers;
    void publish_and_update_supported_energy_transfers();
    bool update_supported_energy_transfers(const std::vector<types::iso15118::EnergyTransferMode>& energy_transfers);
    bool update_supported_energy_transfers(const types::iso15118::EnergyTransferMode& energy_transfer);
    std::mutex hlc_ac_parameters_mutex;
    void update_hlc_ac_parameters();
    // 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 // EVSE_MANAGER_HPP
