#include "template_water_heater.h"
#include "esphome/core/log.h"

namespace esphome::template_ {

static const char *const TAG = "template.water_heater";

TemplateWaterHeater::TemplateWaterHeater() = default;

void TemplateWaterHeater::setup() {
  if (this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE ||
      this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE_AND_CALL) {
    auto restore = this->restore_state_();

    if (restore.has_value()) {
      restore->perform();
    }
  }
  if (!this->current_temperature_f_.has_value() && !this->target_temperature_f_.has_value() &&
      !this->mode_f_.has_value() && !this->away_f_.has_value() && !this->is_on_f_.has_value())
    this->disable_loop();
}

water_heater::WaterHeaterTraits TemplateWaterHeater::traits() {
  water_heater::WaterHeaterTraits traits;

  if (!this->supported_modes_.empty()) {
    traits.set_supported_modes(this->supported_modes_);
    traits.add_feature_flags(water_heater::WATER_HEATER_SUPPORTS_OPERATION_MODE);
  }

  traits.set_supports_current_temperature(true);
  if (this->target_temperature_f_.has_value()) {
    traits.add_feature_flags(water_heater::WATER_HEATER_SUPPORTS_TARGET_TEMPERATURE);
  }
  if (this->away_f_.has_value()) {
    traits.set_supports_away_mode(true);
  }
  if (this->is_on_f_.has_value()) {
    traits.add_feature_flags(water_heater::WATER_HEATER_SUPPORTS_ON_OFF);
  }
  return traits;
}

void TemplateWaterHeater::loop() {
  bool changed = false;

  auto curr_temp = this->current_temperature_f_.call();
  if (curr_temp.has_value()) {
    if (*curr_temp != this->current_temperature_) {
      this->current_temperature_ = *curr_temp;
      changed = true;
    }
  }

  auto target_temp = this->target_temperature_f_.call();
  if (target_temp.has_value()) {
    if (*target_temp != this->target_temperature_) {
      this->target_temperature_ = *target_temp;
      changed = true;
    }
  }

  auto new_mode = this->mode_f_.call();
  if (new_mode.has_value()) {
    if (*new_mode != this->mode_) {
      this->mode_ = *new_mode;
      changed = true;
    }
  }

  auto away = this->away_f_.call();
  if (away.has_value()) {
    if (*away != this->is_away()) {
      this->set_state_flag_(water_heater::WATER_HEATER_STATE_AWAY, *away);
      changed = true;
    }
  }

  auto is_on = this->is_on_f_.call();
  if (is_on.has_value()) {
    if (*is_on != this->is_on()) {
      this->set_state_flag_(water_heater::WATER_HEATER_STATE_ON, *is_on);
      changed = true;
    }
  }

  if (changed) {
    this->publish_state();
  }
}

void TemplateWaterHeater::dump_config() {
  LOG_WATER_HEATER("", "Template Water Heater", this);
  ESP_LOGCONFIG(TAG, "  Optimistic: %s", YESNO(this->optimistic_));
}

float TemplateWaterHeater::get_setup_priority() const { return setup_priority::HARDWARE; }

water_heater::WaterHeaterCallInternal TemplateWaterHeater::make_call() {
  return water_heater::WaterHeaterCallInternal(this);
}

void TemplateWaterHeater::control(const water_heater::WaterHeaterCall &call) {
  auto mode_val = call.get_mode();
  if (mode_val.has_value()) {
    if (this->optimistic_) {
      this->mode_ = *mode_val;
    }
  }
  if (!std::isnan(call.get_target_temperature())) {
    if (this->optimistic_) {
      this->target_temperature_ = call.get_target_temperature();
    }
  }

  auto away_val = call.get_away();
  if (away_val.has_value()) {
    if (this->optimistic_) {
      this->set_state_flag_(water_heater::WATER_HEATER_STATE_AWAY, *away_val);
    }
  }
  auto on_val = call.get_on();
  if (on_val.has_value()) {
    if (this->optimistic_) {
      this->set_state_flag_(water_heater::WATER_HEATER_STATE_ON, *on_val);
    }
  }

  this->set_trigger_.trigger();

  if (this->optimistic_) {
    this->publish_state();
  }
}

}  // namespace esphome::template_
