// SPDX-FileCopyrightText: 2024 Redict Contributors
// SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
//
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-License-Identifier: LGPL-3.0-only

#include "redictmodule.h"

#define UNUSED(V) ((void) V)

int cmd_xadd(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    UNUSED(argv);
    UNUSED(argc);
    RedictModule_ReplyWithSimpleString(ctx, "OK");
    return REDICTMODULE_OK;
}

int RedictModule_OnLoad(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    REDICTMODULE_NOT_USED(argv);
    REDICTMODULE_NOT_USED(argc);
    if (RedictModule_Init(ctx, "cmdintrospection", 1, REDICTMODULE_APIVER_1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"cmdintrospection.xadd",cmd_xadd,"write deny-oom random fast",0,0,0) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    RedictModuleCommand *xadd = RedictModule_GetCommand(ctx,"cmdintrospection.xadd");

    RedictModuleCommandInfo info = {
        .version = REDICTMODULE_COMMAND_INFO_VERSION,
        .arity = -5,
        .summary = "Appends a new message to a stream. Creates the key if it doesn't exist.",
        .since = "5.0.0",
        .complexity = "O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.",
        .tips = "nondeterministic_output",
        .history = (RedictModuleCommandHistoryEntry[]){
            /* NOTE: All versions specified should be the module's versions, not
             * Redict'! We use Redict versions in this example for the purpose of
             * testing (comparing the output with the output of the vanilla
             * XADD). */
            {"6.2.0", "Added the `NOMKSTREAM` option, `MINID` trimming strategy and the `LIMIT` option."},
            {"7.0.0", "Added support for the `<ms>-*` explicit ID form."},
            {0}
        },
        .key_specs = (RedictModuleCommandKeySpec[]){
            {
                .notes = "UPDATE instead of INSERT because of the optional trimming feature",
                .flags = REDICTMODULE_CMD_KEY_RW | REDICTMODULE_CMD_KEY_UPDATE,
                .begin_search_type = REDICTMODULE_KSPEC_BS_INDEX,
                .bs.index.pos = 1,
                .find_keys_type = REDICTMODULE_KSPEC_FK_RANGE,
                .fk.range = {0,1,0}
            },
            {0}
        },
        .args = (RedictModuleCommandArg[]){
            {
                .name = "key",
                .type = REDICTMODULE_ARG_TYPE_KEY,
                .key_spec_index = 0
            },
            {
                .name = "nomkstream",
                .type = REDICTMODULE_ARG_TYPE_PURE_TOKEN,
                .token = "NOMKSTREAM",
                .since = "6.2.0",
                .flags = REDICTMODULE_CMD_ARG_OPTIONAL
            },
            {
                .name = "trim",
                .type = REDICTMODULE_ARG_TYPE_BLOCK,
                .flags = REDICTMODULE_CMD_ARG_OPTIONAL,
                .subargs = (RedictModuleCommandArg[]){
                    {
                        .name = "strategy",
                        .type = REDICTMODULE_ARG_TYPE_ONEOF,
                        .subargs = (RedictModuleCommandArg[]){
                            {
                                .name = "maxlen",
                                .type = REDICTMODULE_ARG_TYPE_PURE_TOKEN,
                                .token = "MAXLEN",
                            },
                            {
                                .name = "minid",
                                .type = REDICTMODULE_ARG_TYPE_PURE_TOKEN,
                                .token = "MINID",
                                .since = "6.2.0",
                            },
                            {0}
                        }
                    },
                    {
                        .name = "operator",
                        .type = REDICTMODULE_ARG_TYPE_ONEOF,
                        .flags = REDICTMODULE_CMD_ARG_OPTIONAL,
                        .subargs = (RedictModuleCommandArg[]){
                            {
                                .name = "equal",
                                .type = REDICTMODULE_ARG_TYPE_PURE_TOKEN,
                                .token = "="
                            },
                            {
                                .name = "approximately",
                                .type = REDICTMODULE_ARG_TYPE_PURE_TOKEN,
                                .token = "~"
                            },
                            {0}
                        }
                    },
                    {
                        .name = "threshold",
                        .type = REDICTMODULE_ARG_TYPE_STRING,
                        .display_text = "threshold" /* Just for coverage, doesn't have a visible effect */
                    },
                    {
                        .name = "count",
                        .type = REDICTMODULE_ARG_TYPE_INTEGER,
                        .token = "LIMIT",
                        .since = "6.2.0",
                        .flags = REDICTMODULE_CMD_ARG_OPTIONAL
                    },
                    {0}
                }
            },
            {
                .name = "id-selector",
                .type = REDICTMODULE_ARG_TYPE_ONEOF,
                .subargs = (RedictModuleCommandArg[]){
                    {
                        .name = "auto-id",
                        .type = REDICTMODULE_ARG_TYPE_PURE_TOKEN,
                        .token = "*"
                    },
                    {
                        .name = "id",
                        .type = REDICTMODULE_ARG_TYPE_STRING,
                    },
                    {0}
                }
            },
            {
                .name = "data",
                .type = REDICTMODULE_ARG_TYPE_BLOCK,
                .flags = REDICTMODULE_CMD_ARG_MULTIPLE,
                .subargs = (RedictModuleCommandArg[]){
                    {
                        .name = "field",
                        .type = REDICTMODULE_ARG_TYPE_STRING,
                    },
                    {
                        .name = "value",
                        .type = REDICTMODULE_ARG_TYPE_STRING,
                    },
                    {0}
                }
            },
            {0}
        }
    };
    if (RedictModule_SetCommandInfo(xadd, &info) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    return REDICTMODULE_OK;
}
