#include <inttypes.h>  // For PRI macros
#include "evse_config.h"
#include "board_config.h"
#include "evse_limits.h"
#include "esp_log.h"
#include "nvs.h"

static const char *TAG = "evse_config";

static nvs_handle_t nvs;

// ========================
// Configurable parameters
// ========================
static uint8_t  max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
static uint16_t charging_current;               // Persisted (NVS)
static uint16_t charging_current_runtime = 0;   // Runtime only
static bool     socket_outlet;
static bool     rcm;
static uint8_t  temp_threshold = 60;
static bool     require_auth;

// ========================
// Initialization
// ========================
esp_err_t evse_config_init(void) {
    ESP_LOGD(TAG, "Initializing NVS configuration...");
    return nvs_open("evse", NVS_READWRITE, &nvs);
}

void evse_check_defaults(void) {
    esp_err_t err;
    uint8_t u8;
    uint16_t u16;
    uint32_t u32;
    bool needs_commit = false;

    ESP_LOGD(TAG, "Checking default parameters...");

    // Max charging current
    err = nvs_get_u8(nvs, "max_chrg_curr", &u8);
    if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT) {
        max_charging_current = MAX_CHARGING_CURRENT_LIMIT;
        nvs_set_u8(nvs, "max_chrg_curr", max_charging_current);
        needs_commit = true;
        ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current);
    } else {
        max_charging_current = u8;
    }

    // Charging current (default, persisted)
    err = nvs_get_u16(nvs, "def_chrg_curr", &u16);
    if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT) || u16 > (max_charging_current)) {
        charging_current = max_charging_current;
        nvs_set_u16(nvs, "def_chrg_curr", charging_current);
        needs_commit = true;
        ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current);
    } else {
        charging_current = u16;
    }

    // Runtime charging current initialized from persisted default
    charging_current_runtime = charging_current;
    ESP_LOGD(TAG, "Runtime charging current initialized to: %d", charging_current_runtime);

    // Auth required
    err = nvs_get_u8(nvs, "require_auth", &u8);
    require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false;
    if (err != ESP_OK) {
        nvs_set_u8(nvs, "require_auth", require_auth);
        needs_commit = true;
    }

    // Socket outlet
    err = nvs_get_u8(nvs, "socket_outlet", &u8);
    socket_outlet = (err == ESP_OK && u8) && board_config.proximity;
    if (err != ESP_OK) {
        nvs_set_u8(nvs, "socket_outlet", socket_outlet);
        needs_commit = true;
    }

    // RCM
    err = nvs_get_u8(nvs, "rcm", &u8);
    rcm = (err == ESP_OK && u8) && board_config.rcm;
    if (err != ESP_OK) {
        nvs_set_u8(nvs, "rcm", rcm);
        needs_commit = true;
    }

    // Temp threshold
    err = nvs_get_u8(nvs, "temp_threshold", &u8);
    temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60;
    if (err != ESP_OK) {
        nvs_set_u8(nvs, "temp_threshold", temp_threshold);
        needs_commit = true;
    }

    // Optional limits
    if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK)
        evse_set_consumption_limit(u32);

    if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK)
        evse_set_charging_time_limit(u32);

    if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK)
        evse_set_under_power_limit(u16);

    // Save to NVS if needed
    if (needs_commit) {
        err = nvs_commit(nvs);
        if (err == ESP_OK) {
            ESP_LOGD(TAG, "Configuration committed to NVS.");
        } else {
            ESP_LOGE(TAG, "Failed to commit configuration to NVS: %s", esp_err_to_name(err));
        }
    }
}

// ========================
// Charging current getters/setters
// ========================
uint8_t evse_get_max_charging_current(void) {
    return max_charging_current;
}

esp_err_t evse_set_max_charging_current(uint8_t value) {
    if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT)
        return ESP_ERR_INVALID_ARG;
    max_charging_current = value;
    nvs_set_u8(nvs, "max_chrg_curr", value);
    return nvs_commit(nvs);
}

uint16_t evse_get_charging_current(void) {
    return charging_current;
}

esp_err_t evse_set_charging_current(uint16_t value) {
    if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
        return ESP_ERR_INVALID_ARG;
    charging_current = value;
    nvs_set_u16(nvs, "def_chrg_curr", value);
    return nvs_commit(nvs);
}

uint16_t evse_get_default_charging_current(void) {
    uint16_t value;
    if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK)
        return value;
    return charging_current;
}

esp_err_t evse_set_default_charging_current(uint16_t value) {
    if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current))
        return ESP_ERR_INVALID_ARG;
    nvs_set_u16(nvs, "def_chrg_curr", value);
    return nvs_commit(nvs);
}

// ========================
// Runtime current (not saved)
// ========================
void evse_set_runtime_charging_current(uint16_t value) {
    
    if (value > (max_charging_current)) {
        value= max_charging_current;
    }

    if (value < (MIN_CHARGING_CURRENT_LIMIT) ) {
        value= MIN_CHARGING_CURRENT_LIMIT;
    }

    charging_current_runtime = value;
    ESP_LOGI(TAG, "Runtime charging current updated: %d", charging_current_runtime);
}

uint16_t evse_get_runtime_charging_current(void) {
    return charging_current_runtime;
}


// ========================
// Socket outlet
// ========================
bool evse_get_socket_outlet(void) {
    return socket_outlet;
}

esp_err_t evse_set_socket_outlet(bool value) {
    if (value && !board_config.proximity)
        return ESP_ERR_INVALID_ARG;
    socket_outlet = value;
    nvs_set_u8(nvs, "socket_outlet", value);
    return nvs_commit(nvs);
}

// ========================
// RCM
// ========================
bool evse_is_rcm(void) {
    return rcm;
}

esp_err_t evse_set_rcm(bool value) {
    if (value && !board_config.rcm)
        return ESP_ERR_INVALID_ARG;
    rcm = value;
    nvs_set_u8(nvs, "rcm", value);
    return nvs_commit(nvs);
}

// ========================
// Temperature
// ========================
uint8_t evse_get_temp_threshold(void) {
    return temp_threshold;
}

esp_err_t evse_set_temp_threshold(uint8_t value) {
    if (value < 40 || value > 80)
        return ESP_ERR_INVALID_ARG;
    temp_threshold = value;
    nvs_set_u8(nvs, "temp_threshold", value);
    return nvs_commit(nvs);
}


// ========================
// Availability
// ========================
static bool is_available = true;

bool evse_config_is_available(void) {
    return is_available;
}

void evse_config_set_available(bool available) {
    is_available = available;
}

// ========================
// Enable/Disable
// ========================
static bool is_enabled = true;

bool evse_config_is_enabled(void) {
    return is_enabled;
}

void evse_config_set_enabled(bool enabled) {
    is_enabled = enabled;
}
