/**
 * @file function_call_menu.c
 * @brief 全局菜单 FC 参考实现：menu_switch 参数路由
 *
 * 云端 schema：
 *   {"function_name":"menu_switch",
 *    "parameter_list":[
 *        {"function":"<动作类别>"},
 *        {"option":"<第一参数>"},
 *        {"param1":"<第二参数>"}
 *    ]}
 */

#include "function_call_menu.h"

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "hal_json.h"
#include "hal_log.h"

#define TAG "FC_MENU"

#define FC_MENU_PAYLOAD_BUF_SZ      128
#define FC_MENU_DEFAULT_VOLUME      30
#define FC_MENU_VOLUME_STEP         10

typedef struct {
    aiagent_engine_t                  engine;
    const char *const                *characters;
    size_t                            character_count;
    const function_call_menu_voice_t *voices;
    size_t                            voice_count;
    size_t                            current_idx;
    int                               volume;
    bool                              inited;
} fc_menu_state_t;

static fc_menu_state_t s_state;

static const char *menu_next_character(void)
{
    if (s_state.character_count == 0u) {
        return NULL;
    }
    s_state.current_idx =
        (s_state.current_idx + 1u) % s_state.character_count;
    return s_state.characters[s_state.current_idx];
}

static const char *menu_find_character(const char *name)
{
    if (!name || s_state.character_count == 0u) {
        return NULL;
    }
    for (size_t i = 0; i < s_state.character_count; ++i) {
        if (strcmp(s_state.characters[i], name) == 0) {
            s_state.current_idx = i;
            return s_state.characters[i];
        }
    }
    return NULL;
}

static int menu_apply_character(const char *role_name)
{
    if (!role_name || !role_name[0]) {
        return -1;
    }
    char payload[FC_MENU_PAYLOAD_BUF_SZ];
    int n = snprintf(payload, sizeof(payload),
                     "{\"scene_role\":\"%s\"}", role_name);
    if (n <= 0 || (size_t)n >= sizeof(payload)) {
        return -1;
    }
    return aiagent_engine_switch_scene_role(s_state.engine, payload);
}

static const char *menu_find_voice(const char *name)
{
    if (!name || s_state.voice_count == 0u) {
        return NULL;
    }
    for (size_t i = 0; i < s_state.voice_count; ++i) {
        if (strcmp(s_state.voices[i].name, name) == 0) {
            return s_state.voices[i].vcn;
        }
    }
    return NULL;
}

static int menu_apply_voice(const char *vcn)
{
    if (!vcn || !vcn[0]) {
        return -1;
    }
    char payload[FC_MENU_PAYLOAD_BUF_SZ];
    int n = snprintf(payload, sizeof(payload),
                     "{\"tts\":\"DEFAULT{\\\"vcn\\\":\\\"%s\\\"}\"}", vcn);
    if (n <= 0 || (size_t)n >= sizeof(payload)) {
        return -1;
    }
    return aiagent_engine_switch_scene_role(s_state.engine, payload);
}

/* 每个动作类别一个 handler，只做受限枚举比较 */
typedef int (*menu_fn_t)(const char *option,
                         const char *param1,
                         const char **err);

static int h_adjust_volume(const char *opt, const char *p1, const char **err)
{
    if (!opt) {
        *err = "missing option";
        return -1;
    }

    int v = s_state.volume;
    if (strcmp(opt, "up") == 0) {
        v += FC_MENU_VOLUME_STEP;
    } else if (strcmp(opt, "down") == 0) {
        v -= FC_MENU_VOLUME_STEP;
    } else if (strcmp(opt, "set") == 0) {
        if (!p1 || p1[0] < '0' || p1[0] > '9') {
            *err = "set needs numeric param1";
            return -1;
        }
        v = atoi(p1);
    } else {
        *err = "bad option";
        return -1;
    }

    if (v < 0)   {
        v = 0;
    }
    if (v > 100) {
        v = 100;
    }

    int ret = aiagent_engine_set_volume(s_state.engine, v);
    if (ret == 0) {
        s_state.volume = v;
    } else {
        *err = "engine set_volume failed";
    }
    return ret;
}

static int h_switch_mode(const char *opt, const char *p1, const char **err)
{
    (void)p1;
    if (!opt) {
        *err = "missing option";
        return -1;
    }

    aiagent_interact_mode_t mode;
    if      (strcmp(opt, "press")      == 0) {
        mode = AIAGENT_INTERACT_PRESS;
    } else if (strcmp(opt, "wakeup")     == 0) {
        mode = AIAGENT_INTERACT_WAKEUP;
    } else if (strcmp(opt, "continuous") == 0) {
        mode = AIAGENT_INTERACT_CONTINUOUS;
    } else {
        *err = "bad option";
        return -1;
    }

    int ret = aiagent_engine_switch_mode(s_state.engine, mode);
    if (ret != 0) {
        *err = "engine switch_mode failed";
    }
    return ret;
}

static int h_switch_role(const char *opt, const char *p1, const char **err)
{
    if (!opt) {
        *err = "missing option";
        return -1;
    }

    const char *role = NULL;
    if (strcmp(opt, "by_name") == 0) {
        role = menu_find_character(p1);
        if (!role) {
            *err = "role not found";
            return -1;
        }
    } else if (strcmp(opt, "next") == 0) {
        role = menu_next_character();
        if (!role) {
            *err = "no characters";
            return -1;
        }
    } else {
        *err = "bad option";
        return -1;
    }

    int ret = menu_apply_character(role);
    if (ret != 0) {
        *err = "apply role failed";
    }
    return ret;
}

static int h_switch_voice(const char *opt, const char *p1, const char **err)
{
    if (!opt) {
        *err = "missing option";
        return -1;
    }

    if (strcmp(opt, "by_name") == 0) {
        if (!p1 || !p1[0]) {
            *err = "param1 required";
            return -1;
        }
        const char *vcn = menu_find_voice(p1);
        if (!vcn) {
            *err = "voice not found";
            return -1;
        }
        int ret = menu_apply_voice(vcn);
        if (ret != 0) {
            *err = "apply voice failed";
        }
        return ret;
    }
    *err = "bad option";
    return -1;
}

static const struct {
    const char *fn;
    menu_fn_t   cb;
} s_dispatch[] = {
    {"adjust_volume", h_adjust_volume},
    {"switch_mode",   h_switch_mode},
    {"switch_role",   h_switch_role},
    {"switch_voice",  h_switch_voice},
};

#define FC_MENU_DISPATCH_CNT (sizeof(s_dispatch) / sizeof(s_dispatch[0]))

static int handle_menu_switch(const char *session_id, const char *params)
{
    if (!s_state.engine) {
        return -1;
    }

    hal_json_t pj = params ? hal_json_parse(params) : NULL;

    char fn_buf[32]  = {0};
    char opt_buf[32] = {0};
    char p1_buf[64]  = {0};
    if (pj) {
        const char *s;
        if ((s = hal_json_get_string(pj, "function")) != NULL) {
            snprintf(fn_buf, sizeof(fn_buf), "%s", s);
        }
        if ((s = hal_json_get_string(pj, "option")) != NULL) {
            snprintf(opt_buf, sizeof(opt_buf), "%s", s);
        }
        if ((s = hal_json_get_string(pj, "param1")) != NULL) {
            snprintf(p1_buf, sizeof(p1_buf), "%s", s);
        }
    }

    const char *fn  = fn_buf[0]  ? fn_buf  : NULL;
    const char *opt = opt_buf[0] ? opt_buf : NULL;
    const char *p1  = p1_buf[0]  ? p1_buf  : NULL;

    int ret = -1;
    const char *err_msg = NULL;

    if (!fn) {
        err_msg = "missing function";
    } else {
        size_t i = 0;
        for (i = 0; i < FC_MENU_DISPATCH_CNT; ++i) {
            if (strcmp(fn, s_dispatch[i].fn) == 0) {
                ret = s_dispatch[i].cb(opt, p1, &err_msg);
                break;
            }
        }
        if (i == FC_MENU_DISPATCH_CNT) {
            err_msg = "unknown function";
        }
    }

    if (pj) {
        hal_json_destroy(pj);
    }

    BD_LOGI(TAG, "menu_switch: fn=%s opt=%s p1=%s ret=%d",
            fn ? fn : "null", opt ? opt : "null",
            p1 ? p1 : "null", ret);

    if (session_id) {
        aiagent_engine_send_fc_result(s_state.engine, session_id,
                                      ret == 0 ? "ok" : "error",
                                      ret == 0 ? "done"
                                               : (err_msg ? err_msg
                                                          : "failed"));
    }
    return ret;
}

int function_call_menu_init(const function_call_menu_cfg_t *cfg)
{
    if (!cfg || !cfg->engine) {
        return -1;
    }
    s_state.engine          = cfg->engine;
    s_state.characters      = cfg->characters;
    s_state.character_count = cfg->characters ? cfg->character_count : 0u;
    s_state.voices          = cfg->voices;
    s_state.voice_count     = cfg->voices ? cfg->voice_count : 0u;
    s_state.current_idx     = 0u;
    s_state.volume          = FC_MENU_DEFAULT_VOLUME;
    s_state.inited          = true;

    BD_LOGI(TAG, "function_call_menu init, character_count=%u, voice_count=%u",
            (unsigned)s_state.character_count,
            (unsigned)s_state.voice_count);
    return 0;
}

void function_call_menu_deinit(void)
{
    memset(&s_state, 0, sizeof(s_state));
}

int function_call_menu_dispatch(const char *session_id,
                                const char *name,
                                const char *params)
{
    if (!s_state.inited || !name) {
        return -1;
    }

    if (strcmp(name, "menu_switch") == 0) {
        handle_menu_switch(session_id, params);
        return 0;
    }

    return -1;
}
