#include "app_controller.h"
#include <string.h>

#define DEFAULT_COMMAND_SPACING_MS 50

// Default UART configuration for MideaUART: 9600 baud, 8N1.
static void apply_default_uart_config(uart_config_t *cfg) {
    cfg->tx_pin = 16;
    cfg->rx_pin = 17;
    cfg->baud_rate = 9600;
    cfg->data_bits = 8;
    cfg->parity = 0;    // none
    cfg->stop_bits = 1;
}

bool app_controller_init(app_controller_t *app, const app_controller_config_t *config) {
    if (!app) {
        return false;
    }

    memset(app, 0, sizeof(*app));

    // Resolve configuration (fall back to defaults where needed).
    if (config) {
        app->config = *config;
    } else {
        memset(&app->config, 0, sizeof(app->config));
    }
    if (app->config.uart_config.baud_rate == 0) {
        apply_default_uart_config(&app->config.uart_config);
    }
    if (app->config.command_spacing_ms == 0) {
        app->config.command_spacing_ms = DEFAULT_COMMAND_SPACING_MS;
    }

    // Bring up the UART driver.
    if (!uart_driver_init(&app->uart, &app->config.uart_config)) {
        return false;
    }

    // Bring up the integration layer on top of the UART driver.
    integration_layer_config_t ilc = {
        .uart_driver = &app->uart,
        .command_queue_size = 16,
        .command_timeout_ms = 1000,
    };
    if (!integration_layer_init(&app->integration, &ilc)) {
        uart_driver_deinit(&app->uart);
        return false;
    }
    app->integration.command_spacing_ms = app->config.command_spacing_ms;

    // Bring up the status monitor.
    if (!status_monitor_init(&app->status_monitor, &app->config.status_config)) {
        integration_layer_deinit(&app->integration);
        uart_driver_deinit(&app->uart);
        return false;
    }

    // Bring up the Zigbee ZCL cluster.
    if (!zigbee_zcl_init()) {
        status_monitor_deinit(&app->status_monitor);
        integration_layer_deinit(&app->integration);
        uart_driver_deinit(&app->uart);
        return false;
    }

    app->commands_sent = 0;
    app->status_updates = 0;
    app->recovery_count = 0;
    app->initialized = true;
    return true;
}

void app_controller_deinit(app_controller_t *app) {
    if (!app) {
        return;
    }
    status_monitor_deinit(&app->status_monitor);
    integration_layer_deinit(&app->integration);
    uart_driver_deinit(&app->uart);
    app->initialized = false;
}

bool app_controller_process_zigbee_command(app_controller_t *app,
                                           uint8_t endpoint, uint16_t cluster_id,
                                           uint8_t command_id, const uint8_t *payload,
                                           uint16_t payload_length,
                                           uint8_t *uart_out, size_t *uart_out_len) {
    if (!app || !app->initialized) {
        if (uart_out_len) {
            *uart_out_len = 0;
        }
        return false;
    }

    size_t out_len = (uart_out_len != NULL) ? *uart_out_len : 0;

    bool ok = integration_layer_handle_zigbee_command(&app->integration,
                                                      endpoint, cluster_id, command_id,
                                                      payload, payload_length,
                                                      uart_out, uart_out ? &out_len : NULL);

    if (uart_out_len) {
        *uart_out_len = ok ? out_len : 0;
    }

    // Count only commands that actually produced an AC-bound UART frame.
    if (ok && uart_out && out_len > 0) {
        app->commands_sent++;
    }

    return ok;
}

bool app_controller_process_ac_status(app_controller_t *app,
                                      const uint8_t *frame, size_t length,
                                      uint32_t current_time,
                                      zcl_thermostat_attrs_t *out_attrs) {
    if (!app || !app->initialized || !frame || length == 0) {
        return false;
    }

    midea_status_t status;
    if (!status_monitor_process_response(&app->status_monitor, frame, length,
                                         current_time, &status)) {
        return false;
    }

    // Map the decoded status to ZCL attributes.
    zcl_thermostat_attrs_t attrs;
    status_monitor_map_to_zcl(&status, &attrs);

    // Push into the Zigbee ZCL cluster so Home Assistant can read it back.
    zigbee_zcl_set_local_temperature(attrs.local_temperature);
    zigbee_zcl_set_system_mode(attrs.system_mode);

    if (out_attrs) {
        *out_attrs = attrs;
    }

    app->status_updates++;
    return true;
}

bool app_controller_poll_status(app_controller_t *app, uint32_t current_time) {
    if (!app || !app->initialized) {
        return false;
    }

    midea_status_t status;
    bool ok = status_monitor_poll(&app->status_monitor, &app->uart,
                                  current_time, &status);
    if (ok) {
        zcl_thermostat_attrs_t attrs;
        status_monitor_map_to_zcl(&status, &attrs);
        zigbee_zcl_set_local_temperature(attrs.local_temperature);
        zigbee_zcl_set_system_mode(attrs.system_mode);
        app->status_updates++;
        return true;
    }

    // Communication failed - let recovery decide whether to reset.
    app_controller_recover_if_needed(app, current_time);
    return false;
}

bool app_controller_reset(app_controller_t *app) {
    if (!app) {
        return false;
    }

    // Re-initialize UART.
    uart_driver_deinit(&app->uart);
    if (!uart_driver_init(&app->uart, &app->config.uart_config)) {
        app->initialized = false;
        return false;
    }

    // Reset integration command state (rebind to the fresh UART driver).
    integration_layer_config_t ilc = {
        .uart_driver = &app->uart,
        .command_queue_size = 16,
        .command_timeout_ms = 1000,
    };
    if (!integration_layer_init(&app->integration, &ilc)) {
        app->initialized = false;
        return false;
    }
    app->integration.command_spacing_ms = app->config.command_spacing_ms;

    // Reset status-monitor counters while preserving configuration.
    if (!status_monitor_init(&app->status_monitor, &app->config.status_config)) {
        app->initialized = false;
        return false;
    }

    app->initialized = true;
    return true;
}

bool app_controller_recover_if_needed(app_controller_t *app, uint32_t current_time) {
    if (!app || !app->initialized) {
        return false;
    }

    bool needs_recovery = app->status_monitor.comm_timeout ||
                          status_monitor_check_timeout(&app->status_monitor, current_time);

    // "Never polled yet" is not a fault condition - don't churn on startup.
    if (needs_recovery && !app->status_monitor.last_poll_success &&
        app->status_monitor.error_count == 0) {
        return false;
    }

    if (needs_recovery) {
        if (app_controller_reset(app)) {
            app->recovery_count++;
            return true;
        }
    }

    return false;
}

bool app_controller_is_healthy(const app_controller_t *app, uint32_t current_time) {
    if (!app || !app->initialized) {
        return false;
    }
    if (status_monitor_has_fault(&app->status_monitor)) {
        return false;
    }
    if (app->status_monitor.comm_timeout) {
        return false;
    }
    // Before the first successful poll the watchdog reports "timed out"; treat
    // that as healthy-until-proven-otherwise so startup isn't flagged as a fault.
    if (!app->status_monitor.last_poll_success) {
        return true;
    }
    return !status_monitor_check_timeout(&app->status_monitor, current_time);
}
