/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstdarg>

#include "cJSON.h"
#include "vibrator_agent.h"
#include "vibrator_agent_type.h"

inline void CliLog(char const* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    int32_t ret = vfprintf(stdout, fmt, args);
    if (ret >= 0) {
        ret = fprintf(stdout, "\n");
    }
    (void)ret;
    va_end(args);
}

void CliError(char const* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    int32_t ret = fprintf(stdout, "[ERROR] ");
    if (ret >= 0) {
        ret = vfprintf(stdout, fmt, args);
    }
    if (ret >= 0) {
        ret = fprintf(stdout, "\n");
    }
    (void)ret;
    va_end(args);
}

constexpr int32_t VIBRATOR_SUCCESS = 0;
constexpr size_t EFFECT_ID_ARG_LEN = 11;
constexpr size_t EQUAL_SIGN_OFFSET = 12;
constexpr size_t MAX_EFFECT_ID_LENGTH = 256;
constexpr int32_t MIN_ARGC_WITH_COMMAND = 2;
constexpr int32_t ARGV_CMD_PARAM_START_INDEX = 2;
constexpr size_t HELP_ARGV_SIZE = 2;

using CommandHandler = std::function<int(int, char**)>;

struct Command {
    const char* name;
    const char* description;
    const char* usage;
    const char* parameters;
    const char* examples;
    CommandHandler handler;
};

static std::unordered_map<std::string, Command> gCommands;
static const char* G_PROGRAM_NAME = "ohos-vibratorControl";
static bool g_hasSubcommand = true;
static const char* G_TOOL_DESCRIPTION = "Vibrator control utility for starting preset vibrations and "
    "checking effect support";

inline void RegisterCommand(Command const& cmd)
{
    gCommands[cmd.name] = cmd;
}

int OutputSuccess(cJSON* data)
{
    cJSON* response = cJSON_CreateObject();
    if (response == nullptr) {
        return 1;
    }

    cJSON_AddItemToObject(response, "type", cJSON_CreateString("result"));
    cJSON_AddItemToObject(response, "status", cJSON_CreateString("success"));
    cJSON_AddItemToObject(response, "data", data);

    char* jsonStr = cJSON_PrintUnformatted(response);
    if (jsonStr != nullptr) {
        std::cout << jsonStr << std::endl;
        free(jsonStr);
    }
    cJSON_Delete(response);
    return std::cout ? 0 : 1;
}

int OutputError(std::string const& code, std::string const& message, std::string const& suggestion = "")
{
    cJSON* response = cJSON_CreateObject();
    if (response == nullptr) {
        return 1;
    }

    cJSON_AddItemToObject(response, "type", cJSON_CreateString("result"));
    cJSON_AddItemToObject(response, "status", cJSON_CreateString("failed"));
    cJSON_AddItemToObject(response, "errCode", cJSON_CreateString(code.c_str()));
    cJSON_AddItemToObject(response, "errMsg", cJSON_CreateString(message.c_str()));
    cJSON_AddItemToObject(response, "suggestion", cJSON_CreateString(suggestion.c_str()));

    char* jsonStr = cJSON_PrintUnformatted(response);
    if (jsonStr != nullptr) {
        std::cout << jsonStr << std::endl;
        free(jsonStr);
    }
    cJSON_Delete(response);
    return 1;
}

std::string ParseEffectId(int32_t argc, char** argv)
{
    std::string effectId;
    for (int32_t i = 0; i < argc; i++) {
        if (strncmp(argv[i], "--effectId", EFFECT_ID_ARG_LEN) == 0) {
            if (argv[i][EFFECT_ID_ARG_LEN] == '=') {
                effectId = argv[i] + EQUAL_SIGN_OFFSET;
            } else if (i + 1 < argc && argv[i + 1][0] != '-') {
                effectId = argv[i + 1];
                i++;
            }
        }
    }
    return effectId;
}

void ShowGeneralHelp()
{
    CliLog("%s - %s", G_PROGRAM_NAME, G_TOOL_DESCRIPTION);
    CliLog("");
    CliLog("Usage:");
    CliLog("  %s [options]", G_PROGRAM_NAME);
    CliLog("  %s <command> [options]", G_PROGRAM_NAME);
    CliLog("");
    CliLog("Parameters:");
    CliLog("  --help             Display this help message");
    CliLog("");
    CliLog("SubCommands:");
    for (const auto& pair : gCommands) {
        CliLog("  %-18s %s", pair.first.c_str(), pair.second.description ? pair.second.description : "");
    }
    CliLog("");
    CliLog("Examples:");
    CliLog("  %s --help", G_PROGRAM_NAME);
    for (const auto& pair : gCommands) {
        if (strcmp(pair.first.c_str(), "help") != 0) {
            CliLog("  %s %s --help", G_PROGRAM_NAME, pair.first.c_str());
            break;
        }
    }
}

int OutputInvalidCommandError(std::string const& targetCmd)
{
    cJSON* response = cJSON_CreateObject();
    if (response == nullptr) {
        return 1;
    }

    cJSON_AddItemToObject(response, "status", cJSON_CreateString("failed"));
    cJSON_AddItemToObject(response, "errCode", cJSON_CreateString("ERR_VIB_INVALID_COMMAND"));
    cJSON_AddItemToObject(response, "errMsg", cJSON_CreateString(("Unknown command: " + targetCmd).c_str()));
    cJSON_AddItemToObject(response, "suggestion",
        cJSON_CreateString("Run 'ohos-vibratorControl --help' to see available commands"));

    char* jsonStr = cJSON_PrintUnformatted(response);
    if (jsonStr != nullptr) {
        std::cout << jsonStr << std::endl;
        free(jsonStr);
    }
    cJSON_Delete(response);
    return 1;
}

void ShowCommandHelp(Command const& cmd)
{
    CliLog("%s %s - %s", G_PROGRAM_NAME, cmd.name, cmd.description ? cmd.description : "N/A");

    if (cmd.usage) {
        CliLog("");
        CliLog("Usage:");
        CliLog("  %s", cmd.usage);
    }

    if (cmd.parameters) {
        CliLog("");
        CliLog("Parameters:");
        CliLog("%s", cmd.parameters);
    }
    CliLog("    %-18s %s", "--help", "Display this help message");

    if (cmd.examples) {
        CliLog("");
        CliLog("Examples:");
        CliLog("%s", cmd.examples);
    }
}

int CmdHelp(int32_t argc, char** argv)
{
    std::string targetCmd;
    for (int32_t i = 1; i < argc; i++) {
        if (argv[i] != nullptr && argv[i][0] != '-') {
            targetCmd = argv[i];
            break;
        }
    }

    if (targetCmd.empty()) {
        ShowGeneralHelp();
        return 0;
    }

    auto it = gCommands.find(targetCmd);
    if (it == gCommands.end()) {
        return OutputInvalidCommandError(targetCmd);
    }

    ShowCommandHelp(it->second);
    return 0;
}

int CmdStartVibrator(int32_t argc, char** argv)
{
    std::string effectId = ParseEffectId(argc, argv);
    if (effectId.empty()) {
        return OutputError("ERR_VIB_ARG_MISSING",
            "Missing required parameter: --effectId",
            "Run 'ohos-vibratorControl startVibrator --help' for usage details");
    }

    if (effectId.length() > MAX_EFFECT_ID_LENGTH) {
        return OutputError("ERR_VIB_ARG_TOO_LONG",
            "EffectId length exceeds maximum limit of 256 characters",
            "Please provide a shorter effect ID");
    }

    int32_t ret = OHOS::Sensors::StartVibrator(effectId.c_str());
    if (ret != VIBRATOR_SUCCESS) {
        return OutputError("ERR_VIB_START_FAILED",
            "Failed to start vibrator with effect: " + effectId,
            "Please check if the effect is supported using "
            "'ohos-vibratorControl isSupportEffect --effectId " + effectId + "'");
    }

    cJSON* result = cJSON_CreateObject();
    if (result == nullptr) {
        return 1;
    }
    cJSON_AddItemToObject(result, "effectId", cJSON_CreateString(effectId.c_str()));
    cJSON_AddItemToObject(result, "status", cJSON_CreateString("vibrating"));
    return OutputSuccess(result);
}

int CmdIsSupportEffect(int32_t argc, char** argv)
{
    std::string effectId = ParseEffectId(argc, argv);
    if (effectId.empty()) {
        return OutputError("ERR_VIB_ARG_MISSING",
            "Missing required parameter: --effectId",
            "Run 'ohos-vibratorControl isSupportEffect --help' for usage details");
    }

    if (effectId.length() > MAX_EFFECT_ID_LENGTH) {
        return OutputError("ERR_VIB_ARG_TOO_LONG",
            "EffectId length exceeds maximum limit of 256 characters",
            "Please provide a shorter effect ID");
    }

    bool state = false;
    int32_t ret = OHOS::Sensors::IsSupportEffect(effectId.c_str(), &state);
    if (ret != VIBRATOR_SUCCESS) {
        return OutputError("ERR_VIB_QUERY_FAILED",
            "Failed to query effect support for: " + effectId,
            "Please check the effect ID format and try again");
    }

    cJSON* result = cJSON_CreateObject();
    if (result == nullptr) {
        return 1;
    }
    cJSON_AddItemToObject(result, "effectId", cJSON_CreateString(effectId.c_str()));
    cJSON_AddItemToObject(result, "isSupported", cJSON_CreateBool(state ? 1 : 0));
    return OutputSuccess(result);
}

void InitCommands()
{
    Command helpCmd = {"help", "Show this help message",
        "ohos-vibratorControl help [command]",
        " command           Optional. Show detailed help for a specific command.",
        " ohos-vibratorControl help\n"
        " ohos-vibratorControl help startVibrator",
        CmdHelp};
    RegisterCommand(helpCmd);

    Command startVibratorCmd = {"startVibrator", "Start preset vibration",
        "ohos-vibratorControl startVibrator --effectId <string>",
        "    --effectId       Required string. The preset vibration effect ID "
        "(e.g., haptic.clock.timer, haptic.long.press)",
        "    ohos-vibratorControl startVibrator --effectId haptic.clock.timer\n"
        "    ohos-vibratorControl startVibrator --effectId haptic.long.press.heavy",
        CmdStartVibrator};
    RegisterCommand(startVibratorCmd);

    Command isSupportEffectCmd = {"isSupportEffect", "Query if a preset effect is supported",
        "ohos-vibratorControl isSupportEffect --effectId <string>",
        "    --effectId       Required string. The preset vibration effect ID to query (e.g., haptic.clock.timer)",
        "    ohos-vibratorControl isSupportEffect --effectId haptic.clock.timer\n"
        "    ohos-vibratorControl isSupportEffect --effectId haptic.long.press.heavy",
        CmdIsSupportEffect};
    RegisterCommand(isSupportEffectCmd);

    g_hasSubcommand = (gCommands.size() > 1);
}

void PrintUsage(const char* prog)
{
    CliError("Usage: %s [options...]", prog);
    CliError("Run '%s help' for more information", prog);
}

int main(int argc, char** argv)
{
    G_PROGRAM_NAME = argv[0];
    if (argc >= MIN_ARGC_WITH_COMMAND && strcmp(argv[1], "--help") == 0) {
        InitCommands();
        char* helpArgv[HELP_ARGV_SIZE] = {argv[0], nullptr};
        CmdHelp(1, helpArgv);
        return 0;
    }

    if (argc < MIN_ARGC_WITH_COMMAND) {
        PrintUsage(argv[0]);
        return 1;
    }

    InitCommands();

    std::string cmdName = argv[1];
    for (int32_t i = ARGV_CMD_PARAM_START_INDEX; i < argc; i++) {
        if (argv[i] != nullptr && strcmp(argv[i], "--help") == 0) {
            char* helpArgv[HELP_ARGV_SIZE] = {argv[0], const_cast<char*>(cmdName.c_str())};
            CmdHelp(MIN_ARGC_WITH_COMMAND, helpArgv);
            return 0;
        }
    }

    auto it = gCommands.find(cmdName);
    if (it == gCommands.end()) {
        CliError("Unknown command: %s", cmdName.c_str());
        PrintUsage(argv[0]);
        return 1;
    }

    return it->second.handler(static_cast<int32_t>(argc - MIN_ARGC_WITH_COMMAND), argv + ARGV_CMD_PARAM_START_INDEX);
}