#pragma once

#include "esphome/core/component.h"
#include "esphome/core/datatypes.h"
#include "esphome/core/defines.h"
#ifdef USE_BUTTON
#include "esphome/components/button/button.h"
#endif
#ifdef USE_NUMBER
#include "esphome/components/number/number.h"
#endif
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"

namespace esphome::bl0940 {

// Caveat: All these values are big endian (low - middle - high)
struct DataPacket {
  uint8_t frame_header;    // Packet header (0x58 in EN docs, 0x55 in CN docs and Tasmota tests)
  uint24_le_t i_fast_rms;  // Fast RMS current
  uint24_le_t i_rms;       // RMS current
  uint24_t RESERVED0;      // Reserved
  uint24_le_t v_rms;       // RMS voltage
  uint24_t RESERVED1;      // Reserved
  int24_le_t watt;         // Active power (can be negative for bidirectional measurement)
  uint24_t RESERVED2;      // Reserved
  uint24_le_t cf_cnt;      // Energy pulse count
  uint24_t RESERVED3;      // Reserved
  uint16_le_t tps1;        // Internal temperature sensor 1
  uint8_t RESERVED4;       // Reserved (should be 0x00)
  uint16_le_t tps2;        // Internal temperature sensor 2
  uint8_t RESERVED5;       // Reserved (should be 0x00)
  uint8_t checksum;        // Packet checksum
} __attribute__((packed));

class BL0940 final : public PollingComponent, public uart::UARTDevice {
 public:
  // Sensor setters
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
  void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
  void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }

  // Temperature sensor setters
  void set_internal_temperature_sensor(sensor::Sensor *internal_temperature_sensor) {
    internal_temperature_sensor_ = internal_temperature_sensor;
  }
  void set_external_temperature_sensor(sensor::Sensor *external_temperature_sensor) {
    external_temperature_sensor_ = external_temperature_sensor;
  }

  // Configuration setters
  void set_legacy_mode(bool enable) { this->legacy_mode_enabled_ = enable; }
  void set_read_command(uint8_t read_command) { this->read_command_ = read_command; }
  void set_write_command(uint8_t write_command) { this->write_command_ = write_command; }

  // Reference value setters (used for calibration and conversion)
  void set_current_reference(float current_ref) { this->current_reference_ = current_ref; }
  void set_energy_reference(float energy_ref) { this->energy_reference_ = energy_ref; }
  void set_power_reference(float power_ref) { this->power_reference_ = power_ref; }
  void set_voltage_reference(float voltage_ref) { this->voltage_reference_ = voltage_ref; }

#ifdef USE_NUMBER
  // Calibration number setters (for Home Assistant number entities)
  void set_current_calibration_number(number::Number *num) { this->current_calibration_number_ = num; }
  void set_voltage_calibration_number(number::Number *num) { this->voltage_calibration_number_ = num; }
  void set_power_calibration_number(number::Number *num) { this->power_calibration_number_ = num; }
  void set_energy_calibration_number(number::Number *num) { this->energy_calibration_number_ = num; }
#endif

  // Resets all calibration values to defaults
  void reset_calibration();

  // Core component methods
  void loop() override;
  void update() override;
  void setup() override;
  void dump_config() override;

 protected:
  // --- Sensor pointers ---
  sensor::Sensor *voltage_sensor_{nullptr};               // Voltage sensor
  sensor::Sensor *current_sensor_{nullptr};               // Current sensor
  sensor::Sensor *power_sensor_{nullptr};                 // Power sensor (can be negative for bidirectional)
  sensor::Sensor *energy_sensor_{nullptr};                // Energy sensor
  sensor::Sensor *internal_temperature_sensor_{nullptr};  // Internal temperature sensor
  sensor::Sensor *external_temperature_sensor_{nullptr};  // External temperature sensor

#ifdef USE_NUMBER
  // --- Calibration number entities (for dynamic calibration via HA UI) ---
  number::Number *voltage_calibration_number_{nullptr};
  number::Number *current_calibration_number_{nullptr};
  number::Number *power_calibration_number_{nullptr};
  number::Number *energy_calibration_number_{nullptr};
#endif

  // --- Internal state ---
  uint32_t prev_cf_cnt_ = 0;       // Previous energy pulse count (for energy calculation)
  float max_temperature_diff_{0};  // Max allowed temperature difference between two measurements (noise filter)

  // --- Reference values for conversion ---
  float power_reference_;        // Divider for raw power to get Watts
  float power_reference_cal_;    // Calibrated power reference
  float voltage_reference_;      // Divider for raw voltage to get Volts
  float voltage_reference_cal_;  // Calibrated voltage reference
  float current_reference_;      // Divider for raw current to get Amperes
  float current_reference_cal_;  // Calibrated current reference
  float energy_reference_;       // Divider for raw energy to get kWh
  float energy_reference_cal_;   // Calibrated energy reference

  // --- Home Assistant calibration values (multipliers, default 1) ---
  float current_cal_{1};
  float voltage_cal_{1};
  float power_cal_{1};
  float energy_cal_{1};

  // --- Protocol commands ---
  uint8_t read_command_;
  uint8_t write_command_;

  // --- Mode flags ---
  bool legacy_mode_enabled_ = true;

  // --- Methods ---
  // Converts packed temperature value to float and updates the sensor
  float update_temp_(sensor::Sensor *sensor, uint16_le_t packed_temperature) const;

  // Validates the checksum of a received data packet
  bool validate_checksum_(DataPacket *data);

  // Handles a received data packet
  void received_package_(DataPacket *data);

  // Calculates reference values for calibration and conversion
  float calculate_energy_reference_();
  float calculate_power_reference_();
  float calculate_calibration_value_(float state);

  // Calibration update callbacks (used with number entities)
  void current_calibration_callback_(float state);
  void voltage_calibration_callback_(float state);
  void power_calibration_callback_(float state);
  void energy_calibration_callback_(float state);
  void reset_calibration_callback_();

  // Recalculates all reference values after calibration changes
  void recalibrate_();
};

}  // namespace esphome::bl0940
