/**
 * @file json_command_runner.h
 * @brief RM JSON 指令执行器接口。
 *
 * @author mashiro
 * @date 2026-07-09
 * @last_modified 2026-07-09
 *
 * @details
 * 本文件声明 JSON 指令执行器。执行器属于 core 层，只负责连接生命周期、
 * 命令流程编排和运动安全校验；真实硬件能力通过 `IRobotArm` 注入，
 * 因而本头文件不包含 RealMan RM_API2 头文件。
 *
 * 设计原则：
 * - core 只依赖接口，不依赖具体 SDK；
 * - 所有运动命令发送前必须统一安全校验；
 * - `dry_run=true` 时只读取当前状态和校验目标，不发送真实运动；
 * - 单条命令执行拆分成小函数，便于后续单元测试和维护。
 *
 * @dependencies
 * - rm_control/common/result.h
 * - rm_control/config/json_command.h
 * - rm_control/interfaces/robot_arm.h
 */
#pragma once

#include "rm_control/common/result.h"
#include "rm_control/config/json_command.h"
#include "rm_control/interfaces/robot_arm.h"

#include <iosfwd>

namespace rm_control::core {

/**
 * @brief JSON 指令执行器。
 *
 * @author mashiro
 * @date 2026-07-09
 * @last_modified 2026-07-09
 *
 * @details
 * 该类把已经解析好的 `JsonCommandProgram` 转换为机械臂接口调用。它不读取文件，
 * 不包含具体 RealMan SDK 类型，只通过 `IRobotArm` 访问硬件能力。当前工程仅面向
 * 睿尔曼 RM75；接口隔离用于控制依赖边界和测试，不作为多品牌机械臂扩展承诺。
 *
 * @note
 * 运动安全策略来自 `JsonCommandProgram::safety`，默认 dry-run 且禁止真实运动。
 *
 * @warning
 * 调用 `run()` 可能连接真实机械臂；调用方必须先确认运行环境安全。
 */
class JsonCommandRunner {
public:
    /**
     * @brief 构造 JSON 指令执行器。
     *
     * @author mashiro
     * @date 2026-07-09
     * @last_modified 2026-07-09
     *
     * @details
     * 构造函数只保存 `IRobotArm` 引用，不连接硬件、不读取状态。这样调用方可以在
     * 测试中注入 fake robot，也可以在生产环境中注入 RealMan driver。
     *
     * @param[in,out] robot 已创建的机械臂抽象接口对象，生命周期必须长于 runner。
     *
     * @return 构造函数无显式返回值。
     *
     * @throws 本构造函数不主动抛出异常。
     *
     * @note
     * `robot` 以引用保存，避免 runner 拥有硬件驱动生命周期。
     *
     * @warning
     * 调用方不能传入即将销毁的临时对象。
     *
     * @thread_safety
     * 构造本身线程安全；后续并发调用同一个 runner 不做保证。
     *
     * @par 边界条件
     * C++ 引用不能为空，因此不做空指针检查。
     *
     * @par 后续维护
     * 如果需要多机械臂支持，应引入更高层 orchestration，不应让本类持有容器。
     */
    explicit JsonCommandRunner(interfaces::IRobotArm &robot);

    /**
     * @brief 执行一个已解析的 JSON 指令程序。
     *
     * @author mashiro
     * @date 2026-07-09
     * @last_modified 2026-07-09
     *
     * @details
     * 该函数先连接机械臂，再按 JSON 中 `commands` 顺序逐条执行。只读命令会输出状态，
     * 运动命令会先读取当前状态并执行限幅校验；如果 `dry_run=true`，运动命令只校验不发送。
     *
     * @param[in] program 已解析并完成字段校验的 JSON 指令程序。
     * @param[out] out 流程日志输出流。
     *
     * @return 成功时返回 `Result<void>::success()`。
     * @return 失败时返回连接、状态读取、安全校验或运动执行错误。
     *
     * @throws 本函数不主动抛出异常；底层接口错误通过 `Result` 返回。
     *
     * @note
     * 本函数不负责解析 JSON 文件，文件解析由 config 层完成。
     *
     * @warning
     * `dry_run=false && allow_motion=true` 时可能发送真实运动指令。
     *
     * @thread_safety
     * 该函数会调用注入的 `IRobotArm`，线程安全取决于具体 driver；默认不并发调用。
     *
     * @par 边界条件
     * 空命令列表通常已由 config 层拒绝；若传入空列表，本函数会连接后直接成功返回。
     *
     * @par 后续维护
     * 新增命令类型时应新增专用执行函数和校验测试，不要扩大本函数职责。
     */
    common::Result<void> run(const config::JsonCommandProgram &program, std::ostream &out);

private:
    /**
     * @brief 执行一条 JSON 命令。
     *
     * @author mashiro
     * @date 2026-07-09
     * @last_modified 2026-07-09
     *
     * @details
     * 该函数负责打印命令头并根据命令类型分发到只读、睡眠或运动执行路径。
     * 单独封装是为了让 `run()` 只保留顶层循环和连接流程。
     *
     * @param[in] program 完整 JSON 程序，提供安全策略。
     * @param[in] command 当前命令。
     * @param[in] command_index 当前命令序号，从 0 开始。
     * @param[in] command_count 命令总数。
     * @param[out] out 流程日志输出流。
     *
     * @return 成功或失败原因。
     *
     * @throws 本函数不主动抛出异常。
     *
     * @note
     * 睡眠命令也在此分发，但实际 sleep 保持在 core 层。
     *
     * @warning
     * 运动命令会继续进入真实硬件执行路径，必须依赖后续安全校验。
     *
     * @thread_safety
     * 不保证并发安全。
     *
     * @par 边界条件
     * 未知枚举值会落入运动路径前的 switch 兜底失败逻辑。
     *
     * @par 后续维护
     * 新增命令类型必须在此处显式分支。
     */
    common::Result<void> runSingleCommand(const config::JsonCommandProgram &program,
                                          const config::ArmCommand &command,
                                          size_t command_index,
                                          size_t command_count,
                                          std::ostream &out);

    /**
     * @brief 执行只读命令。
     *
     * @author mashiro
     * @date 2026-07-09
     * @last_modified 2026-07-09
     *
     * @details
     * 该函数处理机器人信息、当前状态和六维力数据读取。只读命令不会发送运动指令，
     * 适合作为硬件状态诊断和 dry-run 前置检查。
     *
     * @param[in] command 当前只读命令。
     * @param[out] out 状态输出流。
     *
     * @return 成功或底层状态读取错误。
     *
     * @throws 本函数不主动抛出异常。
     *
     * @note
     * 只读命令仍要求机器人已连接。
     *
     * @warning
     * 如果未来新增只读命令，应确保不会触发运动或写寄存器。
     *
     * @thread_safety
     * 线程安全取决于注入的 `IRobotArm`。
     *
     * @par 边界条件
     * 非只读命令传入时返回失败，避免静默忽略。
     *
     * @par 后续维护
     * 输出格式变化可能影响脚本解析，应谨慎修改。
     */
    common::Result<void> runReadOnlyCommand(const config::ArmCommand &command,
                                            std::ostream &out);

    /**
     * @brief 执行运动类命令。
     *
     * @author mashiro
     * @date 2026-07-09
     * @last_modified 2026-07-09
     *
     * @details
     * 该函数统一读取当前状态、执行安全校验、处理 dry-run 和 allow-motion 开关，
     * 然后分发到具体运动命令执行函数。
     *
     * @param[in] program 完整 JSON 程序，提供安全策略。
     * @param[in] command 当前运动命令。
     * @param[out] out 流程日志输出流。
     *
     * @return 成功或状态读取、安全校验、运动执行错误。
     *
     * @throws 本函数不主动抛出异常。
     *
     * @note
     * dry-run 仍会读取当前状态，以验证目标相对当前姿态是否安全。
     *
     * @warning
     * `dry_run=false && allow_motion=true` 时会发送真实运动指令。
     *
     * @thread_safety
     * 线程安全取决于注入的 `IRobotArm`。
     *
     * @par 边界条件
     * 状态读取失败时不会继续执行运动。
     *
     * @par 后续维护
     * 所有新增运动命令都必须接入此函数，不能绕过 safety。
     */
    common::Result<void> runMotionCommand(const config::JsonCommandProgram &program,
                                          const config::ArmCommand &command,
                                          std::ostream &out);

    common::Result<void> validateMotionCommand(const config::JsonCommandProgram &program,
                                               const config::ArmCommand &command,
                                               const common::ArmState &current_state) const;

    common::Result<void> validateMoveJCommand(const config::JsonCommandProgram &program,
                                              const config::ArmCommand &command,
                                              const common::ArmState &current_state) const;

    common::Result<void> validateMoveJDeltaCommand(const config::JsonCommandProgram &program,
                                                   const config::ArmCommand &command,
                                                   const common::ArmState &current_state) const;

    common::Result<void> validateMoveJDeltaStepsCommand(const config::JsonCommandProgram &program,
                                                        const config::ArmCommand &command,
                                                        const common::ArmState &current_state) const;

    common::Result<void> validateMoveLCommand(const config::JsonCommandProgram &program,
                                              const config::ArmCommand &command,
                                              const common::ArmState &current_state) const;

    common::Result<void> executeMoveJCommand(const config::ArmCommand &command,
                                             std::ostream &out);

    common::Result<void> executeMoveJDeltaCommand(const config::ArmCommand &command,
                                                  const common::ArmState &current_state,
                                                  std::ostream &out);

    common::Result<void> executeMoveJDeltaStepsCommand(const config::ArmCommand &command,
                                                       std::ostream &out);

    common::Result<void> executeMoveLCommand(const config::ArmCommand &command,
                                             std::ostream &out);

    interfaces::IRobotArm &robot_;
};

} // namespace rm_control::core
