#pragma once

#include "rm_control/common/result.h"

#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <string>

namespace rm_control::modules::trajectory {

inline constexpr std::size_t kPlaybackReportO6JointCount = 6U;
using PlaybackReportO6Values =
    std::array<std::uint16_t, kPlaybackReportO6JointCount>;

struct ControllerManagedPlaybackReport {
    std::string report_identifier;
    std::filesystem::path source_file;
    std::string source_file_sha256;
    bool returned_from_trajectory_end = false;
    std::filesystem::path return_source_file;
    std::string return_source_file_sha256;
    std::size_t trajectory_point_count = 0U;
    std::int64_t sample_period_ms = 0;
    std::int64_t source_duration_ms = 0;
    bool timestamp_is_derived = true;
    int plan_speed_percent = 0;
    int controller_program_id = 0;
    int return_controller_program_id = 0;
    double return_observed_execution_duration_ms = 0.0;
    std::size_t return_program_status_poll_count = 0U;
    double return_minimum_poll_period_ms = 0.0;
    double return_maximum_poll_period_ms = 0.0;
    double return_average_poll_period_ms = 0.0;
    double return_maximum_absolute_poll_jitter_ms = 0.0;
    int return_final_controller_state = -1;
    double observed_execution_duration_ms = 0.0;
    std::size_t program_status_poll_count = 0U;
    double minimum_poll_period_ms = 0.0;
    double maximum_poll_period_ms = 0.0;
    double average_poll_period_ms = 0.0;
    double maximum_absolute_poll_jitter_ms = 0.0;
    int final_controller_state = -1;
    std::uint8_t o6_device_id = 0U;
    PlaybackReportO6Values target_positions{};
    PlaybackReportO6Values speed_commands{};
    PlaybackReportO6Values torque_commands{};
    PlaybackReportO6Values final_positions{};
};

/**
 * @brief 返回构建目录中的 RM75/O6 控制器托管回放报告目录。
 *
 * @reason 运行数据必须与源码目录分离，集中默认目录也避免 Application 硬编码本机绝对路径。
 *
 * @author 唐永康
 * @date 2026-07-11
 *
 * @return CMake 注入的 runtime_data/trajectory_playback_reports 路径。
 *
 * @calls 不访问文件系统。
 * @safety 只构造路径，不连接设备，也不触发机械臂或 O6 动作。
 * @note 测试可以显式传入其他目录，不依赖此默认值。
 */
std::filesystem::path DefaultTrajectoryPlaybackReportDirectory();

/**
 * @brief 将一次厂商控制器托管回放与 O6 抓握结果保存为不可覆盖 JSON 报告。
 *
 * @reason 厂商控制器内部不暴露逐点下发时间，因此报告明确记录可观测的程序轮询周期、
 *         总执行时长、轨迹摘要和 O6 命令/读回，避免把主机轮询统计误称为逐点 jitter。
 *
 * @author 唐永康
 * @date 2026-07-11
 *
 * @param report 已完成任务的只读结构化结果，所有时间单位为 millisecond。
 * @param output_directory 新报告所在目录；不存在时创建，已有同名报告时拒绝覆盖。
 * @return 成功时返回新 JSON 文件路径；字段非法、目录或写入失败时返回上下文错误。
 *
 * @calls std::filesystem::create_directories 和 std::ofstream。
 * @safety 只写新的构建目录报告，不连接设备，也不会继续、暂停或停止任何动作。
 * @note `report_identifier` 由调用方生成且只允许字母、数字、下划线和连字符；
 *       `maximum_absolute_poll_jitter_ms` 是状态轮询抖动，不是控制器内部轨迹点下发抖动。
 */
common::Result<std::filesystem::path>
WriteControllerManagedPlaybackReportWithoutOverwrite(
    const ControllerManagedPlaybackReport &report,
    const std::filesystem::path &output_directory);

} // namespace rm_control::modules::trajectory
