/*
 * Developed by TComLab
 * Version: v0.1
 * Date: 2025-12-15
 */

#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sdkconfig.h"

#include "dcdc_controller.h"
#include "ina226_monitor.h"
#include "uart_mux.h"
#include "usb_cdc_cli.h"
#include "usb_cdc_log.h"
#include "watch_config.h"
#include "ws2812_status.h"

static const char *TAG = "watch-watch";
static const char HB_MESSAGE[] = "{\"cmd\":\"status\"}\r\n";

static int noop_vprintf(const char *fmt, va_list args)
{
    (void)fmt;
    (void)args;
    return 0;
}

void app_main(void)
{
    esp_log_set_vprintf(noop_vprintf);

    if (watch_config_init() != ESP_OK) {
        ESP_LOGE(TAG, "Не вдалося ініціалізувати конфігурацію");
    } else {
        ESP_LOGI(TAG, "Конфігурацію завантажено");
    }

    if (usb_cdc_cli_init() == ESP_OK) {
        ESP_LOGI(TAG, "USB CDC CLI активовано");
    } else {
        ESP_LOGW(TAG, "Не вдалося запустити USB CLI");
    }

    bool ws_ready = false;
    if (ws2812_status_init() == ESP_OK) {
        ws_ready = true;
    } else {
        ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний");
    }

    const watch_config_t *cfg = watch_config_get();
    const uint32_t start_delay_ms = cfg->heartbeat_start_delay_sec * 1000U;
    TickType_t start_delay = pdMS_TO_TICKS(start_delay_ms);
    if (start_delay > 0) {
        ESP_LOGI(TAG, "Очікування %u с перед стартом опитування", cfg->heartbeat_start_delay_sec);
        if (ws_ready) {
            ws2812_status_set_startup_hold(start_delay_ms);
        }
        vTaskDelay(start_delay);
        if (ws_ready) {
            ws2812_status_set_startup_hold(0);
        }
    }

    ESP_LOGI(TAG, "Запуск watch-watch systems");
    if (dcdc_init() != ESP_OK) {
        ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера");
        if (ws_ready) {
            ws2812_status_set_error(true);
        }
        return;
    }

    if (ws_ready) {
        ws2812_status_refresh_from_dcdc();
    }

    if (ina226_monitor_init() == ESP_OK) {
        ESP_LOGI(TAG, "INA226 моніторинг активовано");
        ina226_monitor_sample(NULL);
    } else {
        ESP_LOGW(TAG, "Моніторинг навантаження недоступний");
    }

    if (uart_mux_init() == ESP_OK) {
        ESP_LOGI(TAG, "UART мультиплексор активовано");
    } else {
        ESP_LOGW(TAG, "UART мультиплексор недоступний");
    }

    const TickType_t mux_timeout = pdMS_TO_TICKS(600);
    TickType_t channel_next_hb[DCDC_CHANNEL_COUNT] = {0};
    bool channel_powered[DCDC_CHANNEL_COUNT] = {false};
    const TickType_t power_on_stagger = pdMS_TO_TICKS(3000);
    ESP_LOGI(TAG, "Початок циклічного опитування всіх каналів");

    while (true) {
        cfg = watch_config_get();
        TickType_t hb_period = pdMS_TO_TICKS(cfg->heartbeat_period_sec * 1000U);
        if (hb_period == 0) {
            hb_period = 1;
        }
        TickType_t now = xTaskGetTickCount();
        size_t channels = dcdc_channel_count();
        for (size_t ch = 0; ch < channels; ++ch) {
            bool powered = dcdc_get_state(ch);
            if (!powered) {
                channel_powered[ch] = false;
                ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch);
                if (dcdc_enable(ch) == ESP_OK) {
                    powered = true;
                    channel_powered[ch] = true;
                    channel_next_hb[ch] = now + hb_period;
                    if (ws_ready) {
                        ws2812_status_set_channel_state(ch, true);
                    }
                    vTaskDelay(power_on_stagger);
                } else {
                    ESP_LOGE(TAG, "Не вдалося ввімкнути канал %d", (int)ch);
                    continue;
                }
            }

            if (!channel_powered[ch]) {
                channel_powered[ch] = true;
                channel_next_hb[ch] = now + hb_period;
                continue;
            }

            now = xTaskGetTickCount();
            if (now < channel_next_hb[ch]) {
                continue;
            }

            ina226_reading_t reading = {0};
            if (ina226_monitor_ready() && ina226_monitor_sample(&reading) == ESP_OK) {
                ESP_LOGI(TAG, "Живлення: %.2f В, %.2f А, %.2f Вт",
                         reading.voltage_v, reading.current_a, reading.power_w);
            }

            if (uart_mux_ready()) {
                esp_err_t tx_err = uart_mux_write(ch,
                                                  (const uint8_t *)HB_MESSAGE,
                                                  sizeof(HB_MESSAGE) - 1,
                                                  mux_timeout);
                if (tx_err != ESP_OK) {
                    ESP_LOGW(TAG, "Не вдалося надіслати heartbeat на канал %d: %s",
                             (int)ch, esp_err_to_name(tx_err));
                } else {
                    uint8_t rx_buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN];
                    size_t received = 0;
                    esp_err_t rx_err = uart_mux_read(ch,
                                                     rx_buffer,
                                                     sizeof(rx_buffer),
                                                     &received,
                                                     mux_timeout);
                    if (rx_err == ESP_OK) {
                        if (received > 0) {
                            ESP_LOGI(TAG, "RX CH%u (%u байт після heartbeat)", (unsigned)ch, (unsigned)received);
                            ESP_LOG_BUFFER_HEX_LEVEL(TAG, rx_buffer, received, ESP_LOG_INFO);
                            uart_mux_process_rx(ch, rx_buffer, received);
                        } else {
                            ESP_LOGW(TAG, "Канал %d не відповів даними на heartbeat", (int)ch);
                            uart_mux_report_miss(ch);
                        }
                    } else if (rx_err == ESP_ERR_TIMEOUT) {
                        ESP_LOGW(TAG, "Час очікування відповіді з каналу %d перевищено", (int)ch);
                        uart_mux_report_miss(ch);
                    } else {
                        ESP_LOGW(TAG, "Помилка читання відповіді CH%u: %s",
                                 (unsigned)ch, esp_err_to_name(rx_err));
                        uart_mux_report_miss(ch);
                    }
                }
            } else {
                ESP_LOGW(TAG, "UART мультиплексор недоступний, очікування...");
            }

            channel_next_hb[ch] = now + hb_period;
        }

        vTaskDelay(pdMS_TO_TICKS(50));
    }
}
