#pragma once

#include "rm_control/common/result.h"
#include "rm_control/common/robot_types.h"
#include "rm_control/core/hardware_execution_gate.h"
#include "rm_control/interfaces/robot_arm.h"
#include "rm_control/modules/trajectory/drag_trajectory_validator.h"
#include "rm_control/modules/trajectory/raw_trajectory_store.h"

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <filesystem>
#include <mutex>
#include <string>
#include <string_view>
#include <thread>
#include <vector>

namespace rm_control::core {

enum class DragTeachServiceState {
    Idle,
    Recording,
    Recorded,
    Saved,
    AtOrigin,
    Replaying,
    Paused,
    Stopped,
    Faulted,
};

struct DragTeachServiceConfig {
    std::filesystem::path storage_root;
    std::string session_name;
    std::chrono::milliseconds sample_period{100}; // 允许 10..1000 ms。
    common::DragTeachStartOptions drag_options;
    modules::trajectory::DragTrajectoryValidationLimits validation_limits;
    double maximum_absolute_force_newton = 100.0; // 外力绝对值上限，newton。
    double maximum_absolute_torque_newton_meter = 20.0; // 外力矩上限，newton_meter。
    std::size_t maximum_sample_count = 100000;
};

struct StopRecordingSummary {
    std::size_t recorded_sample_count = 0;
    modules::trajectory::DragTrajectoryValidationReport validation_report;
};

/**
 * @brief 编排 RM75 拖动示教采样、原始文件固化和厂商原生回放。
 *
 * @reason 将状态机、安全门控、内存采样和文件固化从 Application 与厂商 Adapter 中分离，
 *         使全部业务状态转换可通过 MockRobotArm 单元测试。
 *
 * @author 唐永康
 * @date 2026-07-10
 *
 * @calls IRobotArm、RequireHardwareExecutionPermission、ValidateDragTrajectory、
 *        CreateUniqueRawTrajectorySession 和 FinalizeRawTrajectorySession。
 * @safety Start、MoveToOrigin、Replay 和 Continue 可能触发真实硬件；调用前必须提供完整门控。
 * @note 类不拥有机械臂对象；传入接口的生命周期必须长于本服务。
 */
class DragTeachService {
public:
    /**
     * @brief 构造一个尚未启动录制的拖动示教服务。
     *
     * @reason 服务只借用统一机械臂接口，便于生产代码注入 Adapter、测试注入 Mock。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @param robot 机械臂接口引用；对象生命周期必须长于本服务。
     * @return 构造函数无显式返回值。
     *
     * @calls 不调用机械臂接口。
     * @safety 构造过程不连接设备、不触发硬件动作。
     * @note 服务初始状态为 Idle。
     */
    explicit DragTeachService(interfaces::IRobotArm &robot);
    DragTeachService(const DragTeachService &) = delete;
    DragTeachService &operator=(const DragTeachService &) = delete;
    /**
     * @brief 停止仍在进行的采样、拖动或回放并释放线程资源。
     *
     * @reason RAII 收尾避免提前返回后遗留采样线程或机械臂拖动模式。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 析构函数无返回值且不会向外抛出异常。
     *
     * @calls IRobotArm::stopDragTeach、IRobotArm::stopDragTrajectoryReplay。
     * @safety 只尝试减险停止动作，不启动或继续运动。
     * @note 停止错误无法从析构函数返回，由显式 Stop 方法负责可审计处理。
     */
    ~DragTeachService() noexcept;

    /**
     * @brief 完成型号、状态和门控检查后开始拖动示教与内存采样。
     *
     * @reason Start 与 Stop 分离后，操作者可以在明确的 Recording 状态中手动拖动机械臂。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @param config 唯一目录、采样周期、拖动模式及轨迹校验上限。
     * @param gate_input 来自命令行、配置、设备状态与人工确认的安全事实。
     * @return 启动硬件和采样线程成功时返回成功，否则保持非 Recording 状态。
     *
     * @calls IRobotArm::getRobotInfo、getRobotSafetyStatus、startDragTeach。
     * @safety 会让真实 RM75 进入拖动示教；所有门控条件缺一即拒绝。
     * @note 默认 OrdinaryRecorded；六维力复合模式的厂商文件保存仅为 best-effort。
     */
    common::Result<void> StartRecording(
        const DragTeachServiceConfig &config,
        const HardwareExecutionGateInput &gate_input);

    /**
     * @brief 停止采样和拖动示教，并校验完整七关节轨迹。
     *
     * @reason 无论采样是否失败都必须先退出硬件拖动状态，再决定轨迹能否保存或回放。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 成功时返回样本数和轨迹峰值报告；停止或校验失败时进入 Faulted。
     *
     * @calls IRobotArm::stopDragTeach 和 ValidateDragTrajectory。
     * @safety Stop 是减险动作，不依赖新的执行授权。
     * @note 必须在 Recording 状态调用。
     */
    common::Result<StopRecordingSummary> StopRecording();

    /**
     * @brief 将最后一次已校验录制保存为唯一原始目录并生成 manifest。
     *
     * @reason 原始厂商文件、同步 CSV、返回码、大小和 SHA-256 必须在同一不可覆盖会话中追溯。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 文件固化流程成功时返回产物；厂商返回非零仍返回带失败码的 manifest 产物。
     *
     * @calls IRobotArm::saveDragTeachTrajectory、writeTrajectoryCsv、
     *        CreateUniqueRawTrajectorySession 和 FinalizeRawTrajectorySession。
     * @safety 会调用厂商保存接口但不会移动机械臂。
     * @note 只有厂商返回 0、原始文件存在且同步 CSV 成功时状态才进入 Saved。
     */
    common::Result<modules::trajectory::RawTrajectoryArtifact> SaveRawTrajectory();

    /**
     * @brief 让机械臂移动到控制器内最后拖动轨迹的起点。
     *
     * @reason 厂商要求原生回放前先到达起点，单独暴露可避免 Replay 隐式触发额外运动。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @param gate_input 完整安全事实，配置速度上限必须允许厂商固定的 20%。
     * @return 到达起点成功时进入 AtOrigin，否则保持 Saved/Stopped。
     *
     * @calls IRobotArm::moveToDragTrajectoryOrigin。
     * @safety 会以厂商固定 20% 速度触发真实运动，必须完整门控。
     * @note 只支持控制器当前内存中的最后拖动轨迹，不从磁盘加载。
     */
    common::Result<void> MoveToTrajectoryOrigin(
        const HardwareExecutionGateInput &gate_input);

    /**
     * @brief 非阻塞开始回放控制器内最后拖动轨迹。
     *
     * @reason 非阻塞启动才能在同一服务生命周期内提供 Pause、Continue 和 Stop。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @param gate_input 完整安全事实，并包含无速度参数风险接受及独立实测速度验证。
     * @return 启动成功时进入 Replaying。
     *
     * @calls IRobotArm::replayLastDragTrajectory。
     * @safety 会触发真实轨迹运动，必须完整门控且轨迹已通过校验。
     * @note 原生接口没有文件路径和速度参数。
     */
    common::Result<void> ReplayLastDragTrajectory(
        const HardwareExecutionGateInput &gate_input);

    /**
     * @brief 暂停正在执行的原生拖动轨迹。
     *
     * @reason Pause 是减险动作，不应因授权在运动期间失效而被阻止。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 暂停成功时进入 Paused。
     * @calls IRobotArm::pauseDragTrajectoryReplay。
     * @safety 只降低当前运动风险，不需要新的执行授权。
     * @note 仅允许在 Replaying 状态调用。
     */
    common::Result<void> PauseReplay();

    /**
     * @brief 在重新通过完整门控后继续暂停的原生回放。
     *
     * @reason Continue 会恢复真实运动，因此不能复用回放开始时的旧授权。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @param gate_input 调用时重新收集的完整安全事实。
     * @return 继续成功时回到 Replaying。
     * @calls IRobotArm::continueDragTrajectoryReplay。
     * @safety 会恢复真实运动，必须重新完整门控。
     * @note 仅允许在 Paused 状态调用。
     */
    common::Result<void> ContinueReplay(
        const HardwareExecutionGateInput &gate_input);

    /**
     * @brief 停止正在回放或暂停的原生轨迹。
     *
     * @reason Stop 必须在安全授权失效时仍可尝试，防止门控反而阻断减险操作。
     *
     * @author 唐永康
     * @date 2026-07-10
     *
     * @return 停止成功时进入 Stopped。
     * @calls IRobotArm::stopDragTrajectoryReplay。
     * @safety 是减险动作，不依赖新的执行授权。
     * @note 仅允许在 Replaying 或 Paused 状态调用。
     */
    common::Result<void> StopReplay();

    /**
     * @brief 返回当前服务状态快照。
     * @reason 状态机编排与测试需要只读观察，不允许直接修改内部状态。
     * @author 唐永康
     * @date 2026-07-10
     * @return 调用时刻的状态枚举。
     * @calls 不调用设备接口。
     * @safety 纯内存读取，不触发硬件动作。
     * @note 返回后状态可能被另一个线程推进。
     */
    DragTeachServiceState state() const noexcept;

    /**
     * @brief 返回当前内存中的同步 RM75 样本数。
     * @reason Application 需要显示录制进度，但不应取得可变轨迹容器。
     * @author 唐永康
     * @date 2026-07-10
     * @return 已采集并保存在内存中的样本数量。
     * @calls 不调用设备接口。
     * @safety 纯内存读取，不触发硬件动作。
     * @note 录制期间该数值会增长。
     */
    std::size_t recordedSampleCount() const;

private:
    common::Result<void> ValidateRecordingConfig(
        const DragTeachServiceConfig &config) const;
    common::Result<void> ReadAndValidateRobotPreflight(
        const DragTeachServiceConfig &config);
    common::Result<void> ValidateRobotIdentity(
        const common::RobotInfo &info,
        const common::DragTeachStartOptions &options) const;
    common::Result<void> ValidateRobotSafetyStatus(
        const common::RobotSafetyStatus &status) const;
    common::Result<void> ValidateCurrentStateAndForce();
    common::Result<void> ValidateRuntimeMotionPreflight();
    common::Result<void> ValidateSampledArmState(
        const common::ArmState &state) const;
    common::Result<void> ValidateCurrentStateMatchesTrajectoryOrigin();
    common::Result<void> StartHardwareAndSamplingThread();
    void SamplingLoop() noexcept;
    void RunSamplingLoop();
    void RecordSamplingFailure(std::string_view message) noexcept;
    void RequestSamplingStop() noexcept;
    common::Result<void> JoinSamplingThread();
    common::Result<void> StopDragTeachIfActive();
    common::Result<StopRecordingSummary> ValidateStoppedRecording();
    HardwareExecutionGateRequirements OriginRequirements() const;
    HardwareExecutionGateRequirements ReplayRequirements() const;
    common::Result<void> RequirePermission(
        const HardwareExecutionGateInput &gate_input,
        const HardwareExecutionGateRequirements &requirements) const;
    common::Result<void> RequireState(
        DragTeachServiceState expected,
        const std::string &operation_name) const;

    interfaces::IRobotArm &robot_;
    mutable std::mutex operation_mutex_;
    mutable std::mutex mutex_;
    std::condition_variable sampling_condition_;
    std::thread sampling_thread_;
    std::atomic<bool> sampling_stop_requested_{false};
    std::atomic<bool> sampling_failed_{false};
    std::atomic<DragTeachServiceState> state_{DragTeachServiceState::Idle};
    DragTeachServiceConfig config_;
    common::RobotInfo robot_info_;
    common::RobotSoftwareInfo software_info_;
    common::RobotJointLimits joint_limits_;
    std::vector<common::TrajectorySample> samples_;
    modules::trajectory::DragTrajectoryValidationReport validation_report_;
    modules::trajectory::RawTrajectoryArtifact last_artifact_;
    bool hardware_drag_active_ = false;
    bool validation_report_available_ = false;
    bool artifact_available_ = false;
    bool native_motion_possibly_active_ = false;
    std::string sampling_error_;
};

} // namespace rm_control::core
