#pragma once

#include "rm_control/common/result.h"

#include <cstdint>
#include <filesystem>
#include <string>

namespace rm_control::modules::trajectory {

struct RawTrajectorySessionPaths {
    std::filesystem::path directory;
    std::filesystem::path raw_trajectory;
    std::filesystem::path synchronized_csv;
    std::filesystem::path manifest;
    std::string session_id;
    std::string saved_at_utc;
};

struct RawTrajectoryManifestMetadata {
    std::string robot_model;
    std::string sdk_version;
    std::string firmware_version;
    std::uint64_t trajectory_point_count = 0;
    std::uint64_t recorded_sample_count = 0;
    bool vendor_save_api_called = false;
    int vendor_save_return_code = 0;
    std::string vendor_save_error_message;
    bool synchronized_csv_write_succeeded = false;
};

struct RawTrajectoryArtifact {
    RawTrajectorySessionPaths paths;
    RawTrajectoryManifestMetadata metadata;
    bool raw_file_present = false;
    std::uintmax_t raw_file_size_bytes = 0;
    std::string raw_file_sha256;
    std::string manifest_written_at_utc;
};

/**
 * @brief 将操作者提供的会话名清理为安全的 ASCII 文件名片段。
 *
 * @reason 会话名来自外部输入，统一折叠非法字符、限制长度并规避 Windows 保留名，可以让同一轨迹目录在 Linux 和 Windows 上安全复用。
 *
 * @author 唐永康
 * @date 2026-07-10
 *
 * @param requested_session_name 原始会话名；允许为空、包含空白或非 ASCII 字符。
 * @return 最长 64 字符的非空 ASCII 名称，只包含字母、数字、下划线和连字符。
 *
 * @calls 不调用厂商接口；仅执行确定性的字符串清理。
 * @safety 不访问文件系统，不连接机械臂，也不会触发任何硬件动作。
 * @note 连续非法字符折叠为单个下划线；空结果使用 `trajectory`。
 */
std::string SanitizeRawTrajectorySessionName(const std::string &requested_session_name);

/**
 * @brief 在 `storage_root/sessions` 下原子创建一个不会覆盖既有数据的唯一会话目录。
 *
 * @reason 厂商保存接口需要先拿到目标路径；先用 `create_directory` 独占目录，可以在并发采集和同名会话场景下保持原始轨迹不可覆盖。
 *
 * @author 唐永康
 * @date 2026-07-10
 *
 * @param storage_root 轨迹存储根目录；不得为空，父目录可尚未存在。
 * @param requested_session_name 操作者提供的会话名，函数会先进行安全清理。
 * @return 成功时返回会话目录、原始轨迹、同步 CSV、manifest、会话 ID 和 UTC 保存时间；失败时返回文件系统上下文。
 *
 * @calls std::filesystem::create_directories 和 std::filesystem::create_directory。
 * @safety 只创建新的本地目录，不连接机械臂，也不会触发任何硬件动作。
 * @note 会话 ID 格式为 `<清理名>_<UTC毫秒>[_NNN]`，所有产物路径都位于新目录内。
 */
common::Result<RawTrajectorySessionPaths>
CreateUniqueRawTrajectorySession(const std::filesystem::path &storage_root,
                                 const std::string &requested_session_name);

/**
 * @brief 校验厂商原始文件、计算大小和 SHA-256、移除写权限并原子生成 manifest。
 *
 * @reason 把不可变保护和 manifest 固化放在同一收尾边界，可以确保成功或失败的厂商保存尝试都有可审计记录，同时不改写厂商原始内容。
 *
 * @author 唐永康
 * @date 2026-07-10
 *
 * @param paths 由 `CreateUniqueRawTrajectorySession` 返回的受控会话路径。
 * @param metadata 机械臂型号、SDK/固件版本、点数、同步样本数和厂商保存返回码。
 * @return 成功时返回完整原始轨迹产物；即使厂商保存失败且原始文件不存在，也会成功生成 manifest。文件系统或摘要失败时返回上下文错误。
 *
 * @calls CalculateFileSha256、std::filesystem::file_size、std::filesystem::permissions 和 std::filesystem::create_hard_link。
 * @safety 只读取并保护本地轨迹文件，不连接机械臂，也不会触发任何硬件动作。
 * @note 已存在的 manifest 不会被覆盖；完成后原始文件、同步 CSV、manifest 和会话目录均移除全部写权限。
 */
common::Result<RawTrajectoryArtifact>
FinalizeRawTrajectorySession(const RawTrajectorySessionPaths &paths,
                             const RawTrajectoryManifestMetadata &metadata);

} // namespace rm_control::modules::trajectory
