#pragma once

#include "rm_control/common/result.h"
#include "rm_control/common/robot_types.h"

#include <filesystem>
#include <vector>

namespace rm_control::interfaces {

// 机械臂抽象接口。
// core 只依赖这个接口，不直接依赖 RealManRobotArm 或 rm_interface.h。
// 当前支持边界只覆盖睿尔曼 RM75；保留接口是为了隔离 SDK 头文件和便于测试，
// 不是为了引入其他机械臂品牌适配。
class IRobotArm {
public:
    /**
     * @brief 销毁机械臂接口对象。
     * @reason 通过虚析构函数保证经接口释放适配器时正确回收 SDK 资源。
     * @author 唐永康
     * @date 2026-07-10
     * @return 无返回值，析构不得抛出异常。
     * @calls 具体适配器析构函数。
     * @safety 只执行资源清理，不主动发送运动指令。
     * @note 接口不拥有外部安全门控对象。
     */
    virtual ~IRobotArm() = default;

    /**
     * @brief 建立机械臂连接。
     * @reason 将连接和 SDK 初始化隔离在适配器中，使服务可注入 Mock。
     * @author 唐永康
     * @date 2026-07-10
     * @param config 控制器 IP、端口和超时，超时单位为毫秒。
     * @return 成功返回空结果，失败返回带上下文的连接错误。
     * @calls 具体实现调用厂商 SDK 初始化与连接接口。
     * @safety 只建立通信，不应触发运动。
     * @note 重复连接的资源释放策略由实现负责。
     */
    virtual common::Result<void> connect(const common::RobotConnectionConfig &config) = 0;

    /**
     * @brief 释放机械臂连接和 SDK 资源。
     * @reason 统一硬件资源生命周期，防止 C SDK 句柄泄漏。
     * @author 唐永康
     * @date 2026-07-10
     * @return 无返回值，且实现不得抛出异常。
     * @calls 具体实现调用厂商断开和销毁接口。
     * @safety 不发送运动指令。
     * @note 必须允许重复调用。
     */
    virtual void disconnect() = 0;

    /**
     * @brief 查询当前是否持有有效连接。
     * @reason 安全门控必须在所有硬件动作前检查连接状态。
     * @author 唐永康
     * @date 2026-07-10
     * @return 已初始化且句柄有效时返回 true。
     * @calls 不调用厂商接口。
     * @safety 纯只读查询，不触发运动。
     * @note 本函数不得抛出异常。
     */
    virtual bool isConnected() const noexcept = 0;

    /**
     * @brief 查询机械臂型号、自由度、力传感器与控制器代际。
     * @reason 上层安全检查必须使用厂商无关字段确认 RM75 和七自由度。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回机械臂基础信息，失败返回带接口名的读取错误。
     * @calls 具体实现调用厂商机械臂信息查询接口。
     * @safety 只读，不触发运动。
     * @note 上层应优先使用 is_rm75 等语义字段，避免比较厂商枚举整数。
     */
    virtual common::Result<common::RobotInfo> getRobotInfo() = 0;

    /**
     * @brief 查询 SDK、控制器固件及各软件组件版本。
     * @reason manifest 需要记录可追溯的软件环境，且不同组件版本不能混写。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回版本信息，失败返回带接口名的读取错误。
     * @calls 具体实现调用 SDK 版本与控制器软件信息接口。
     * @safety 只读，不触发运动。
     * @note firmware_version 的具体来源由适配器文档明确。
     */
    virtual common::Result<common::RobotSoftwareInfo> getRobotSoftwareInfo() = 0;

    /**
     * @brief 读取七个关节角、TCP 位姿和非零机械臂错误码。
     * @reason 轨迹采样和运动前首点检查需要同一份带明确单位的状态快照。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回当前状态，失败返回带接口名的读取错误。
     * @calls 具体实现调用厂商当前机械臂状态接口。
     * @safety 只读，不触发运动。
     * @note 关节角单位为 degree，位置为 meter，姿态为 radian。
     */
    virtual common::Result<common::ArmState> getCurrentArmState() = 0;

    /**
     * @brief 读取六维力传感器的四组力和力矩数据。
     * @reason 真实运动前检查需要可验证的当前末端受力状态。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回力数据，失败返回带接口名的读取错误。
     * @calls 具体实现调用厂商六维力查询接口。
     * @safety 只读，不触发运动。
     * @note 力单位为 newton，力矩单位为 newton_meter。
     */
    virtual common::Result<common::ForceData> getForceData() = 0;

    /**
     * @brief 聚合运行模式、上电、关节、控制器和安全功能状态。
     * @reason 安全门控需要一次 fail-closed 的厂商无关状态快照。
     * @author 唐永康
     * @date 2026-07-10
     * @return 全部必要查询成功时返回状态，任一查询失败时返回失败。
     * @calls 具体实现调用模式、电源、全状态、控制器、虚拟墙和自碰撞查询接口。
     * @safety 只读，不触发运动；不得推断 SDK 未提供的急停状态。
     * @note virtual_wall_enabled 和 self_collision_enabled 仅表示厂商原始返回值。
     */
    virtual common::Result<common::RobotSafetyStatus> getRobotSafetyStatus() = 0;

    /**
     * @brief 查询控制器当前生效的 RM75 七轴位置、速度和加速度限制。
     * @reason 运动前检查和离线轨迹校验必须使用设备实际限制，不能凭记忆硬编码。
     * @author 唐永康
     * @date 2026-07-10
     * @return 全部四组限制读取成功时返回七轴限制，否则返回具体厂商查询错误。
     * @calls 具体实现调用厂商关节最小/最大位置、最大速度和最大加速度查询接口。
     * @safety 只读，不触发运动，也不会修改控制器限制。
     * @note RM_API2 v1.1.5 的加速度 getter/setter 单位注释不一致；本接口按函数语义使用 degree/second^2，Live 验收前仍需与厂商确认。
     */
    virtual common::Result<common::RobotJointLimits> getJointLimits() = 0;

    /**
     * @brief 发送 RM75 七关节绝对角度运动指令。
     * @reason 将厂商数组转换和长度校验集中在适配器边界。
     * @author 唐永康
     * @date 2026-07-10
     * @param joints_deg 七个绝对关节角，单位 degree。
     * @param speed_percent 厂商速度百分比。
     * @param blend_radius 厂商轨迹交融半径参数。
     * @param trajectory_connect 厂商轨迹连接标志。
     * @param blocking 是否等待运动结束。
     * @return 成功返回空结果，失败返回校验或厂商接口错误。
     * @calls 具体实现调用厂商关节运动接口。
     * @safety 会触发真实运动，必须由上层状态机和安全门控授权。
     * @note 本接口不替代轨迹限位、速度、加速度和工作空间校验。
     */
    virtual common::Result<void> moveJ(const std::vector<float> &joints_deg,
                                       int speed_percent,
                                       int blend_radius,
                                       int trajectory_connect,
                                       bool blocking) = 0;

    /**
     * @brief 发送绝对 TCP 直线运动指令。
     * @reason 将统一 Pose 显式转换为厂商位姿结构，防止单位与 SDK 类型泄漏。
     * @author 唐永康
     * @date 2026-07-10
     * @param pose 目标 TCP 位姿，位置 meter，欧拉姿态 radian。
     * @param speed_percent 厂商速度百分比。
     * @param blend_radius 厂商轨迹交融半径参数。
     * @param trajectory_connect 厂商轨迹连接标志。
     * @param blocking 是否等待运动结束。
     * @return 成功返回空结果，失败返回校验或厂商接口错误。
     * @calls 具体实现调用厂商直线运动接口。
     * @safety 会触发真实运动，必须由上层状态机和安全门控授权。
     * @note 本接口不替代工作空间、虚拟墙和力限制校验。
     */
    virtual common::Result<void> moveL(const common::Pose &pose,
                                       int speed_percent,
                                       int blend_radius,
                                       int trajectory_connect,
                                       bool blocking) = 0;

    /**
     * @brief 启动普通记录拖动或六维力位置姿态拖动示教。
     * @reason 上层只表达拖动语义，厂商模式整数由适配器集中维护。
     * @author 唐永康
     * @date 2026-07-10
     * @param options 拖动模式、厂商记录开关和奇异墙选项。
     * @return 成功返回空结果，失败返回厂商接口错误。
     * @calls 普通模式或六维力复合拖动厂商接口。
     * @safety 会改变真实机械臂控制模式，必须完成硬件安全门控和人工确认。
     * @note 六维力接口不提供厂商轨迹记录参数，保存能力只能 best-effort。
     */
    virtual common::Result<void> startDragTeach(const common::DragTeachStartOptions &options) = 0;

    /**
     * @brief 停止当前拖动示教。
     * @reason 采样成功或失败后都必须通过统一接口退出拖动状态。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回空结果，失败返回厂商接口错误。
     * @calls 具体实现调用厂商停止拖动示教接口。
     * @safety 属于减险操作；拖动已启动时应优先尝试执行。
     * @note 上层应保留采样错误和停止错误两者的上下文。
     */
    virtual common::Result<void> stopDragTeach() = 0;

    /**
     * @brief 将控制器内最后一次拖动轨迹保存到指定原始文件。
     * @reason manifest 必须保留实际返回码，即使厂商保存失败也不能丢失调用记录。
     * @author 唐永康
     * @date 2026-07-10
     * @param path 新建会话目录中的厂商原始轨迹路径。
     * @return 返回完整保存尝试，包括是否调用、返回码、点数、路径和错误文本。
     * @calls 具体实现调用厂商轨迹保存接口。
     * @safety 不触发运动，但要求有效连接和已结束的拖动记录。
     * @note 调用方必须保证目标路径唯一且不覆盖已有原始文件。
     */
    virtual common::VendorTrajectorySaveAttempt
    saveDragTeachTrajectory(const std::filesystem::path &path) = 0;

    /**
     * @brief 控制机械臂移动到控制器内最后一次拖动轨迹的起点。
     * @reason 厂商原生回放要求先到达记录起点，故作为独立受控步骤暴露。
     * @author 唐永康
     * @date 2026-07-10
     * @param blocking 是否等待运动结束。
     * @return 成功返回空结果，失败返回厂商接口错误。
     * @calls 具体实现调用厂商拖动轨迹起点接口。
     * @safety 会以厂商固定 20% 速度触发真实运动，必须通过完整安全门控。
     * @note 安全配置允许速度低于 20% 时，上层必须拒绝调用。
     */
    virtual common::Result<void> moveToDragTrajectoryOrigin(bool blocking) = 0;

    /**
     * @brief 回放控制器内最后一次拖动示教轨迹。
     * @reason 保持厂商原生回放能力，同时由服务状态机实施强制门控。
     * @author 唐永康
     * @date 2026-07-10
     * @param blocking 是否等待回放结束。
     * @return 成功返回空结果，失败返回厂商接口错误。
     * @calls 具体实现调用厂商拖动轨迹回放接口。
     * @safety 会触发真实运动，必须通过完整安全门控且已到达轨迹起点。
     * @note 该接口不能从磁盘重新加载任意轨迹文件。
     */
    virtual common::Result<void> replayLastDragTrajectory(bool blocking) = 0;

    /**
     * @brief 暂停正在执行的厂商拖动轨迹回放。
     * @reason 将暂停纳入服务状态机，禁止业务层绕过流程直接调用 SDK。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回空结果，失败返回厂商接口错误。
     * @calls 具体实现调用厂商暂停回放接口。
     * @safety 属于减险操作，应允许在授权状态变化后继续尝试。
     * @note 仅在回放状态有效。
     */
    virtual common::Result<void> pauseDragTrajectoryReplay() = 0;

    /**
     * @brief 继续已暂停的厂商拖动轨迹回放。
     * @reason 继续动作会恢复真实运动，必须与暂停接口采用不同门控策略。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回空结果，失败返回厂商接口错误。
     * @calls 具体实现调用厂商继续回放接口。
     * @safety 会恢复真实运动，必须重新通过完整安全门控。
     * @note 仅在暂停状态有效。
     */
    virtual common::Result<void> continueDragTrajectoryReplay() = 0;

    /**
     * @brief 停止厂商拖动轨迹回放。
     * @reason 所有回放退出路径都必须通过统一减险操作终止运动。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回空结果，失败返回厂商接口错误。
     * @calls 具体实现调用厂商停止回放接口。
     * @safety 属于减险操作，应允许在授权状态变化后继续尝试。
     * @note 停止后是否需要回安全位由更高层任务状态机决定。
     */
    virtual common::Result<void> stopDragTrajectoryReplay() = 0;

    /**
     * @brief 仅保存磁盘中的 RM75 厂商拖动轨迹程序，不启动运动。
     * @reason 反向返回和正向复现必须在运动前分别占用独立程序槽，避免上传即运动。
     * @author 唐永康
     * @date 2026-07-11
     * @param request 已校验的文件路径、保存速度百分比和控制器程序编号。
     * @return 槽位为空、保存成功且保存后能查询到该编号时返回成功。
     * @calls 具体实现调用 RM_API2 工程发送和在线编程列表查询接口。
     * @safety 固定 only_save=1，不触发机械臂运动，也不覆盖已有控制器程序。
     * @note 该接口不解析轨迹内容；调用方仍须提前完成全量轨迹校验。
     */
    virtual common::Result<void> saveVendorDragTrajectoryProgram(
        const common::VendorDragTrajectoryExecutionRequest &request) = 0;

    /**
     * @brief 运行控制器中已保存的指定程序。
     * @reason 将程序保存与真实运动解耦，使上层可先确认两个程序槽均准备完成。
     * @author 唐永康
     * @date 2026-07-11
     * @param request 程序编号、显式速度百分比和阻塞方式。
     * @return 控制器接受运行请求时返回成功，否则返回参数或 SDK 错误。
     * @calls 具体实现调用 RM_API2 指定编号程序运行接口。
     * @safety 会触发真实运动，必须由上层安全门控授权并校验当前位置接近首点。
     * @note speed_percent 必须为 1..100；blocking=false 支持非阻塞状态监控。
     */
    virtual common::Result<void> runControllerProgram(
        const common::ControllerProgramRunRequest &request) = 0;

    /**
     * @brief 上传并执行磁盘中的 RM75 厂商拖动轨迹工程文件。
     * @reason sourceTXT 文件不能由“回放控制器最后轨迹”接口加载，必须通过独立接口
     *         明确表达本地文件、控制器程序编号和受限速度。
     * @author 唐永康
     * @date 2026-07-10
     * @param request 已校验的文件路径、计划速度百分比和控制器程序编号。
     * @return 上传并成功启动时返回成功，否则返回路径校验、控制器错误行或 SDK 错误。
     * @calls 具体实现调用 RM_API2 工程发送接口，工程类型固定为拖动轨迹。
     * @safety 会触发真实机械臂运动，调用前必须通过完整安全门控并到达轨迹首点。
     * @note 该接口不解析或校验轨迹内容；调用方必须提前完成全量校验与校验值确认。
     * @deprecated 新流程应先调用 saveVendorDragTrajectoryProgram，再调用 runControllerProgram；
     *             本接口仅为兼容既有调用链保留。
     */
    virtual common::Result<void> executeVendorDragTrajectoryFile(
        const common::VendorDragTrajectoryExecutionRequest &request) = 0;

    /**
     * @brief 查询控制器在线编程的当前运行状态。
     * @reason 本地任务状态机必须确认轨迹确实进入运行态并正常结束，不能仅凭上传成功推断完成。
     * @author 唐永康
     * @date 2026-07-10
     * @return 成功返回厂商原始状态、程序编号、轨迹编号和速度；失败返回 SDK 查询错误。
     * @calls 具体实现调用 RM_API2 在线编程运行状态查询接口。
     * @safety 只读，不触发运动。
     * @note 未知 raw_state 必须由上层 fail-closed 处理。
     */
    virtual common::Result<common::ControllerProgramRunState>
    getControllerProgramRunState() = 0;

    /**
     * @brief 以厂商慢停止方式终止当前机械臂运动。
     * @reason 轨迹监控超时、未知状态或后续设备检查失败时需要统一减险出口。
     * @author 唐永康
     * @date 2026-07-10
     * @return SDK 接受停止指令时成功，否则返回带接口名的错误。
     * @calls 具体实现调用 RM_API2 慢停止接口。
     * @safety 属于减险动作，不应被普通运动门控阻止。
     * @note 调用成功只表示控制器接受请求，现场操作者仍须观察设备实际停止状态。
     */
    virtual common::Result<void> slowStopMotion() = 0;

    /**
     * @brief 查询控制器程序编号是否尚未被已有工程占用。
     * @reason 上传厂商轨迹会保存到控制器编号，必须在移动到首点前拒绝覆盖已有程序。
     * @author 唐永康
     * @date 2026-07-10
     * @param controller_program_id 待使用的编号，有效范围 1..100。
     * @return 查询成功时返回可用标志；列表查询或输入失败时返回带上下文错误。
     * @calls 具体实现调用 RM_API2 在线编程列表查询接口。
     * @safety 只读，不触发运动，也不删除或覆盖控制器文件。
     * @note 上传前适配器仍须再次检查，以关闭预检与上传之间的竞争窗口。
     */
    virtual common::Result<bool> isControllerProgramSlotAvailable(
        int controller_program_id) = 0;
};

} // namespace rm_control::interfaces
