#pragma once

#include "rm_control/common/result.h"

#include <cstddef>
#include <cstdint>
#include <vector>

namespace rm_control::interfaces {

/**
 * @brief RM75 工具端 Modbus 总线的当前只读配置快照。
 *
 * @reason O6 readiness 必须依据实际工具端口、电压和通信参数，而不能由业务层用布尔值伪造。
 *
 * @author 唐永康
 * @date 2026-07-10
 *
 * @safety `connected` 只表示总线连接事实，不代表 O6 已通过身份、温度和故障检查。
 * @note `timeout_units` 沿用 RM_API2 的百毫秒单位。
 */
struct ToolModbusBusStatus {
    bool connected = false;
    int port = 0;
    int mode = 0;
    int baudrate = 0;
    int voltage_type = 0;
    int timeout_units = 0;
};

/**
 * @brief RM75 工具端 Modbus RTU 会话的显式初始化参数。
 * @reason 控制器保存的 mode 状态不能替代机械臂启动后的 `rm_set_modbus_mode` 调用，
 *         因此每个真实 O6 会话必须在读写寄存器前主动初始化。
 * @author 唐永康
 * @date 2026-07-11
 * @safety 配置端口和供电但不写 O6 动作寄存器，也不移动机械臂。
 */
struct ToolModbusBusConfiguration {
    int port = 1;
    int baudrate = 115200;
    int voltage_type = 3;
    int timeout_units = 10;
    bool reset_existing_mode = true;
    bool power_cycle_tool = false;
    int power_off_delay_ms = 500;
    int startup_delay_ms = 2000;
};

/**
 * @brief 隔离 RM75 工具端 Modbus 实现的厂商无关接口。
 *
 * @reason LinkerHandAdapter 只应表达 O6 寄存器协议，RM_API2 句柄、结构体和函数必须留在
 *         RealMan 厂商适配层。
 *
 * @author 唐永康
 * @date 2026-07-11
 *
 * @safety 读取方法不触发设备动作；写保持寄存器可能触发真实 O6 关节运动。
 * @note 接口不负责自动重试，调用失败必须原样返回给上层状态机。
 */
class IToolModbusBus {
public:
    /**
     * @brief 销毁工具端 Modbus 总线接口对象。
     *
     * @reason 允许业务层通过接口安全释放具体总线实现。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 析构函数无返回值且不得抛出异常。
     *
     * @calls 具体实现的析构函数。
     * @safety 只释放资源，不发送新的 O6 动作命令。
     * @note 具体实现负责自身连接资源的所有权。
     */
    virtual ~IToolModbusBus() = default;

    /**
     * @brief 显式初始化 RM75 工具端供电和 Modbus RTU 主站会话。
     * @reason RM_API2 要求机械臂启动后在任何寄存器读写前调用 Modbus 配置接口。
     * @author 唐永康
     * @date 2026-07-11
     * @param configuration 端口、波特率、供电、超时和是否先关闭既有模式。
     * @return 成功时返回控制器实际读回的总线状态；任一设置或读回不一致时失败。
     * @calls 具体实现的工具电压、关闭/启动 Modbus 和状态查询接口。
     * @safety 不发送 O6 位置、速度或转矩命令；可能重新初始化工具端通信会话。
     */
    virtual common::Result<ToolModbusBusStatus> Configure(
        const ToolModbusBusConfiguration &configuration) = 0;

    /**
     * @brief 读取 RM75 工具端 Modbus 和供电配置。
     *
     * @reason O6 readiness 必须确认端口 1、主站模式、115200 波特率、24V 和有效超时均已生效。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 成功时返回当前总线状态；任一底层查询失败时返回带接口上下文的错误。
     *
     * @calls 具体 RealMan 实现的工具电压和 RS485 状态查询接口。
     * @safety 只读，不触发机械臂或 O6 运动。
     * @note 返回成功不代表目标 Modbus 从站已经响应。
     */
    virtual common::Result<ToolModbusBusStatus> GetStatus() = 0;

    /**
     * @brief 使用 Modbus 功能码 04 读取单个输入寄存器。
     * @reason RM_API2 的单寄存器接口和多寄存器接口走不同代码路径，诊断时交叉读取可区分
     *         O6 无响应与多寄存器解码问题。
     * @author 唐永康
     * @date 2026-07-11
     * @param device_id Modbus RTU 从站地址，合法范围 1..247。
     * @param address 输入寄存器 PDU 地址，不使用 30001 风格的人机显示地址。
     * @return 成功时返回一个无符号 16 位寄存器值；失败时保留 RM_API2 返回码上下文。
     * @calls 具体 RealMan 实现的单输入寄存器读取接口。
     * @safety 只读，不触发机械臂或 O6 运动。
     * @note 不自动重试，避免把连续超时误判为设备响应。
     */
    virtual common::Result<std::uint16_t> ReadInputRegister(
        std::uint8_t device_id,
        std::uint16_t address) = 0;

    /**
     * @brief 使用 Modbus 功能码 04 读取连续输入寄存器。
     *
     * @reason O6 的位置、转矩、速度、温度、故障和身份均位于输入寄存器，集中接口可避免
     *         LinkerHand 协议层接触 RM_API2 参数结构体。
     *
     * @author 唐永康
     * @date 2026-07-11
     *
     * @param device_id Modbus RTU 从站地址。
     * @param start_address 首个输入寄存器地址。
     * @param register_count 要读取的 16 位寄存器数量，必须大于零。
     * @return 成功时返回按地址递增排列的寄存器值；通信失败时返回原始上下文。
     *
     * @calls 具体 RealMan 实现的多输入寄存器读取接口。
     * @safety 只读，不触发机械臂或 O6 运动。
     * @note 实现必须把 Modbus 字节序转换为 16 位寄存器值。
     */
    virtual common::Result<std::vector<std::uint16_t>> ReadInputRegisters(
        std::uint8_t device_id,
        std::uint16_t start_address,
        std::size_t register_count) = 0;

    /**
     * @brief 使用 Modbus 功能码 03 读取连续保持寄存器。
     *
     * @reason O6 速度和转矩在位置动作下发前必须精确读回，独立的功能码 03 接口使
     *         LinkerHand 协议层能够验证写入结果，同时继续隔离 RM_API2 类型。
     *
     * @author 唐永康
     * @date 2026-07-11
     *
     * @param device_id Modbus RTU 从站地址。
     * @param start_address 首个保持寄存器地址。
     * @param register_count 要读取的 16 位寄存器数量，必须大于零。
     * @return 成功时返回按地址递增排列的寄存器值；通信失败时返回带接口上下文的错误。
     *
     * @calls 具体 RealMan 实现的多保持寄存器读取接口。
     * @safety 只读，不触发机械臂或 O6 运动。
     * @note 实现必须把 Modbus 高低字节转换为 16 位寄存器值，且不得自动重试。
     */
    virtual common::Result<std::vector<std::uint16_t>> ReadHoldingRegisters(
        std::uint8_t device_id,
        std::uint16_t start_address,
        std::size_t register_count) = 0;

    /**
     * @brief 使用 Modbus 功能码 16 写入连续保持寄存器。
     *
     * @reason O6 六关节位置、转矩和速度命令位于保持寄存器 0..17，统一写接口使上层可在
     *         Mock 中验证地址、从站和数据，同时阻止厂商 API 泄漏。
     *
     * @author 唐永康
     * @date 2026-07-11
     *
     * @param device_id Modbus RTU 从站地址。
     * @param start_address 首个保持寄存器地址。
     * @param values 按地址递增排列的 16 位寄存器值；调用期间只读且不转移所有权。
     * @return 写入成功时返回成功；通信或设备拒绝时返回带上下文的失败。
     *
     * @calls 具体 RealMan 实现的多保持寄存器写接口。
     * @safety 可能立即触发真实 O6 关节运动，调用前必须通过状态机和硬件安全门控。
     * @note 本接口不自动重试，避免超时后重复执行不确定的手部动作。
     */
    virtual common::Result<void> WriteHoldingRegisters(
        std::uint8_t device_id,
        std::uint16_t start_address,
        const std::vector<std::uint16_t> &values) = 0;
};

} // namespace rm_control::interfaces
