#pragma once

#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/components/bedjet/bedjet_child.h"
#include "esphome/components/bedjet/bedjet_codec.h"
#include "esphome/components/bedjet/bedjet_hub.h"
#include "esphome/components/climate/climate.h"

#ifdef USE_ESP32

namespace esphome::bedjet {

class BedJetClimate final : public climate::Climate, public BedJetClient, public PollingComponent {
 public:
  void setup() override;
  void loop() override;
  void update() override;
  void dump_config() override;
  float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }

  /* BedJetClient status update */
  void on_status(const BedjetStatusPacket *data) override;
  void on_bedjet_state(bool is_ready) override;
  std::string describe() override;

  /** Sets the default strategy to use for climate::CLIMATE_MODE_HEAT. */
  void set_heating_mode(BedjetHeatMode mode) { this->heating_mode_ = mode; }
  /** Sets the temperature source to use for the climate entity's current temperature */
  void set_temperature_source(BedjetTemperatureSource source) { this->temperature_source_ = source; }

  climate::ClimateTraits traits() override {
    auto traits = climate::ClimateTraits();
    traits.add_feature_flags(climate::CLIMATE_SUPPORTS_ACTION | climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE);
    traits.set_supported_modes({
        climate::CLIMATE_MODE_OFF,
        climate::CLIMATE_MODE_HEAT,
        // climate::CLIMATE_MODE_TURBO // Not supported by Climate: see presets instead
        climate::CLIMATE_MODE_FAN_ONLY,
        climate::CLIMATE_MODE_DRY,
    });

    traits.set_supported_presets({
        // If we support NONE, then have to decide what happens if the user switches to it (turn off?)
        // climate::CLIMATE_PRESET_NONE,
        // Climate doesn't have a "TURBO" mode, but we can use the BOOST preset instead.
        climate::CLIMATE_PRESET_BOOST,
    });
    // Custom fan modes and presets are set once in setup(), stored on Climate base class,
    // and wired automatically via get_traits()
    traits.set_visual_min_temperature(19.0);
    traits.set_visual_max_temperature(43.0);
    traits.set_visual_temperature_step(1.0);
    return traits;
  }

 protected:
  void control(const climate::ClimateCall &call) override;

  BedjetHeatMode heating_mode_ = HEAT_MODE_HEAT;
  BedjetTemperatureSource temperature_source_ = TEMPERATURE_SOURCE_AMBIENT;

  void reset_state_();
  bool update_status_();

  bool is_valid_() {
    // FIXME: find a better way to check this?
    return !std::isnan(this->current_temperature) && !std::isnan(this->target_temperature) &&
           this->current_temperature > 1 && this->target_temperature > 1;
  }
};

}  // namespace esphome::bedjet

#endif
