/**
 * @file trajectory_convert_cli_config.cpp
 * @brief RM75 轨迹 CSV 转 C++ waypoint 程序命令行参数解析实现。
 *
 * @author mashiro
 * @date 2026-07-09
 * @last_modified 2026-07-09
 *
 * @details
 * 本文件实现 `rm_trajectory_convert` 的参数解析和默认输出路径推导。该工具是离线工具，
 * 不连接真实机械臂；解析函数也不读取 CSV 文件，文件存在性和格式错误由 trajectory
 * 模块在加载阶段返回。
 *
 * 设计原则：
 * - 参数解析和文件转换分离；
 * - 默认输出路径由 trajectory 存储模块统一生成；
 * - 不在解析函数中打印或退出；
 * - 保持 `<input_csv> [output_cpp]` 命令格式不变。
 *
 * @dependencies
 * - rm_control/config/trajectory_convert_cli_config.h
 * - rm_control/modules/trajectory/trajectory_file_store.h
 */
#include "rm_control/config/trajectory_convert_cli_config.h"

#include "rm_control/modules/trajectory/trajectory_file_store.h"

#include <ostream>
#include <string>

namespace rm_control::config {
namespace {

/**
 * @brief 将进程名转换为可显示文本。
 *
 * @author mashiro
 * @date 2026-07-09
 * @last_modified 2026-07-09
 *
 * @details
 * usage 输出需要程序名，但测试或异常调用可能传入空指针。该函数统一处理空值，
 * 让错误路径也能输出明确命令名。
 *
 * @param[in] program_name `argv[0]`，允许为空。
 * @param[in] fallback_name 默认程序名。
 *
 * @return 可显示程序名。
 *
 * @throws 本函数不主动抛出异常；字符串分配失败由标准库报告。
 *
 * @note
 * 保留原始路径，不截取 basename。
 *
 * @warning
 * 不访问文件系统，避免 usage 输出依赖运行环境。
 *
 * @thread_safety
 * 函数只使用局部对象，线程安全。
 *
 * @par 边界条件
 * 空指针或空字符串返回 fallback。
 *
 * @par 后续维护
 * 可在公共 utils 稳定后统一复用。
 */
std::string SafeProgramName(const char *program_name, const char *fallback_name) {
    if (program_name == nullptr || program_name[0] == '\0') {
        return fallback_name;
    }
    return program_name;
}

/**
 * @brief 从路径中提取文件主名。
 *
 * @author mashiro
 * @date 2026-07-09
 * @last_modified 2026-07-09
 *
 * @details
 * 默认输出 C++ 文件名需要基于输入 CSV 文件名生成 session name。该函数只做字符串处理，
 * 不访问文件系统，因此输入文件即使不存在也可以得到可预测的默认输出路径。
 *
 * @param[in] path 输入路径字符串，可以是相对路径或绝对路径。
 *
 * @return 去掉目录和最后一个扩展名后的文件主名。
 *
 * @throws 本函数不主动抛出异常；字符串操作失败由标准库报告。
 *
 * @note
 * 仅识别 `/` 作为路径分隔符，符合当前 Linux RM 子工程环境。
 *
 * @warning
 * Windows 反斜杠路径不会被当作目录分隔符；如需跨平台支持需扩展此函数。
 *
 * @thread_safety
 * 函数不使用全局状态，线程安全。
 *
 * @par 边界条件
 * 无扩展名时返回文件名本身；点号出现在目录名前时不视作扩展名。
 *
 * @par 后续维护
 * 如需支持多扩展名规则，应保持该函数单一职责，只做路径主名提取。
 */
std::string FileStem(const std::string &path) {
    const size_t slash_position = path.find_last_of('/');
    const size_t begin_position = slash_position == std::string::npos ? 0 : slash_position + 1;
    const size_t dot_position = path.find_last_of('.');
    const size_t end_position =
        dot_position == std::string::npos || dot_position < begin_position ? path.size() : dot_position;
    return path.substr(begin_position, end_position - begin_position);
}

/**
 * @brief 根据输入 CSV 路径生成默认 C++ 输出路径。
 *
 * @author mashiro
 * @date 2026-07-09
 * @last_modified 2026-07-09
 *
 * @details
 * 该函数使用 trajectory 模块的存储目录规则生成默认 C++ 输出路径。如果默认目录创建失败，
 * 函数退化为当前工作目录下的 `<session>_tcp_path.cpp`，避免 CLI 解析阶段直接失败。
 *
 * @param[in] input_csv_path 输入 CSV 路径。
 * @param[in] session_name 已清洗过的会话名。
 *
 * @return 默认输出 C++ 文件路径。
 *
 * @throws 本函数不主动抛出异常；字符串和路径构造失败由标准库报告。
 *
 * @note
 * 真正写文件时仍会再次校验父目录和写权限。
 *
 * @warning
 * 该函数可能创建默认轨迹目录，因为 `makeTrajectoryFileSet()` 会确保目录存在。
 *
 * @thread_safety
 * 函数本身无全局状态；并发创建同一目录由底层 `mkdir` 处理。
 *
 * @par 边界条件
 * 默认目录创建失败时返回相对 fallback 文件名。
 *
 * @par 后续维护
 * 如果不希望解析阶段创建目录，应调整 trajectory 模块，提供只构造路径的函数。
 */
std::string BuildDefaultOutputPath(const std::string &input_csv_path,
                                   const std::string &session_name) {
    (void)input_csv_path;
    auto files = rm_control::modules::trajectory::makeTrajectoryFileSet(
        rm_control::modules::trajectory::defaultTrajectoryStorageRoot(), session_name);
    if (!files.ok) {
        return session_name + "_tcp_path.cpp";
    }
    return files.value.converted_cpp_path;
}

} // namespace

void PrintTrajectoryConvertUsage(const char *program_name, std::ostream &out) {
    const std::string display_name = SafeProgramName(program_name, "rm_trajectory_convert");
    out << "Usage: " << display_name << " <input_csv> [output_cpp]\n"
        << "\n"
        << "Converts recorded CSV into a C++ TCP waypoint table.\n"
        << "This offline tool does not connect to the robot.\n";
}

common::Result<TrajectoryConvertCliConfig> ParseTrajectoryConvertCliArguments(int argc, char **argv) {
    TrajectoryConvertCliConfig config;
    if (argc == 2) {
        const std::string first_argument = argv[1] == nullptr ? "" : argv[1];
        if (first_argument == "-h" || first_argument == "--help") {
            config.help_requested = true;
            return common::Result<TrajectoryConvertCliConfig>::success(config);
        }
    }

    if (argc < 2 || argc > 3) {
        return common::Result<TrajectoryConvertCliConfig>::failure(
            "expected <input_csv> and optional [output_cpp]");
    }

    config.input_csv_path = argv[1] == nullptr ? "" : argv[1];
    if (config.input_csv_path.empty()) {
        return common::Result<TrajectoryConvertCliConfig>::failure("input_csv path must not be empty");
    }

    config.session_name =
        rm_control::modules::trajectory::sanitizeSessionName(FileStem(config.input_csv_path));
    config.output_cpp_path = argc == 3
        ? std::string(argv[2] == nullptr ? "" : argv[2])
        : BuildDefaultOutputPath(config.input_csv_path, config.session_name);
    if (config.output_cpp_path.empty()) {
        return common::Result<TrajectoryConvertCliConfig>::failure("output_cpp path must not be empty");
    }

    return common::Result<TrajectoryConvertCliConfig>::success(config);
}

} // namespace rm_control::config
