#pragma once

#include "rm_interface.h"

#include <array>
#include <cerrno>
#include <chrono>
#include <cmath>
#include <cstdarg>
#include <cstdio>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <thread>

#ifndef LINKERHAND_PROJECT_ROOT
#define LINKERHAND_PROJECT_ROOT "."
#endif

namespace realman_hw_test {

// 这些常量是当前实验台的固定接入参数。测试代码统一从这里取值，
// 避免每个模块各自硬编码 IP、端口和 RM75 自由度。
constexpr const char *kRobotIp = "192.168.1.18";
constexpr int kRobotPort = 8080;
constexpr int kTimeoutMs = 1000;
constexpr int kExpectedDof = 7;

inline void apiLogCallback(const char *message, va_list args) {
    if (message == nullptr) {
        return;
    }

    char buffer[1024] = {};
    std::vsnprintf(buffer, sizeof(buffer), message, args);
    std::cerr << "[rm_api] " << buffer << '\n';
}

inline int destroyApi() {
#if defined(REALMAN_RM_API_HAS_RM_DESTROY)
    return rm_destroy();
#else
    // 部分 RM_API2 头文件把 destroy 拼成 rm_destory，CMake 会按头文件内容注入宏。
    return rm_destory();
#endif
}

inline bool ensureDirectory(const std::string &path) {
    if (::mkdir(path.c_str(), 0755) == 0 || errno == EEXIST) {
        return true;
    }
    std::cerr << "Failed to create directory: " << path << '\n';
    return false;
}

inline std::string timestampString() {
    const std::time_t now = std::time(nullptr);
    std::tm local_time = {};
    localtime_r(&now, &local_time);

    std::ostringstream out;
    out << std::put_time(&local_time, "%Y%m%d_%H%M%S");
    return out.str();
}

inline std::string reportsDirectory() {
    const std::string root = LINKERHAND_PROJECT_ROOT;
    const std::string reports = root + "/reports";
    const std::string hardware_reports = reports + "/realman_hardware_tests";
    ensureDirectory(reports);
    ensureDirectory(hardware_reports);
    return hardware_reports;
}

inline const char *forceTypeName(rm_force_type_e type) {
    switch (type) {
    case RM_MODEL_RM_B_E:
        return "Standard";
    case RM_MODEL_RM_ZF_E:
        return "1D force";
    case RM_MODEL_RM_SF_E:
        return "6D force";
    case RM_MODEL_RM_ISF_E:
        return "Integrated 6D force";
    default:
        return "Unknown";
    }
}

inline void sleepMs(int milliseconds) {
    std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}

inline bool isFinite(float value) {
    return std::isfinite(static_cast<double>(value));
}

inline bool isFiniteArray(const float *values, int count) {
    if (values == nullptr || count <= 0) {
        return false;
    }
    for (int i = 0; i < count; ++i) {
        if (!isFinite(values[i])) {
            return false;
        }
    }
    return true;
}

inline std::string formatArray(const float *values, int count, const char *prefix = "") {
    std::ostringstream out;
    out << std::fixed << std::setprecision(3);
    for (int i = 0; i < count; ++i) {
        out << (i == 0 ? "[" : ", ") << prefix << (i + 1) << "=" << values[i];
    }
    out << "]";
    return out.str();
}

class TestSuite {
public:
    explicit TestSuite(const std::string &name)
        : name_(name) {
        std::cout.setf(std::ios::unitbuf);
        std::cerr.setf(std::ios::unitbuf);

        const std::string dir = reportsDirectory();
        latest_report_path_ = dir + "/" + name_ + "_latest.md";
        timestamped_report_path_ = dir + "/" + name_ + "_" + timestampString() + ".md";
        latest_report_.open(latest_report_path_);
        timestamped_report_.open(timestamped_report_path_);
        writeLine("# " + name_ + "\n");
        writeLine("| Check | Result | Detail |");
        writeLine("| --- | --- | --- |");
    }

    void info(const std::string &key, const std::string &value) {
        std::cout << "[INFO] " << key << ": " << value << '\n';
        writeLine("| " + key + " | INFO | " + value + " |");
    }

    bool expect(bool condition, const std::string &check, const std::string &detail = "") {
        ++assertions_;
        if (!condition) {
            ++failures_;
        }

        std::cout << (condition ? "[PASS] " : "[FAIL] ") << check;
        if (!detail.empty()) {
            std::cout << " - " << detail;
        }
        std::cout << '\n';

        writeLine("| " + check + " | " + (condition ? "PASS" : "FAIL") + " | " + detail + " |");
        return condition;
    }

    int finish() {
        std::ostringstream summary;
        summary << "\nAssertions: " << assertions_ << ", failures: " << failures_ << "\n";
        writeLine(summary.str());
        std::cout << summary.str();
        std::cout << "Report: " << latest_report_path_ << '\n';
        return failures_ == 0 ? 0 : 1;
    }

private:
    void writeLine(const std::string &line) {
        if (latest_report_) {
            latest_report_ << line << '\n';
        }
        if (timestamped_report_) {
            timestamped_report_ << line << '\n';
        }
    }

    std::string name_;
    std::string latest_report_path_;
    std::string timestamped_report_path_;
    std::ofstream latest_report_;
    std::ofstream timestamped_report_;
    int assertions_ = 0;
    int failures_ = 0;
};

class RobotSession {
public:
    explicit RobotSession(TestSuite *suite)
        : suite_(suite) {}

    RobotSession(const RobotSession &) = delete;
    RobotSession &operator=(const RobotSession &) = delete;

    ~RobotSession() {
        // 每个硬件测试都独立建立/释放 SDK 会话，保证模块之间没有隐藏顺序依赖。
        if (handle_ != nullptr) {
            const int delete_result = rm_delete_robot_arm(handle_);
            if (delete_result != 0 && suite_ != nullptr) {
                suite_->expect(false, "rm_delete_robot_arm", "error=" + std::to_string(delete_result));
            }
            handle_ = nullptr;
        }
        if (initialized_) {
            const int destroy_result = destroyApi();
            if (destroy_result != 0 && suite_ != nullptr) {
                suite_->expect(false, "rm_destroy", "error=" + std::to_string(destroy_result));
            }
        }
    }

    bool connect() {
        rm_set_log_call_back(apiLogCallback, 2);
        rm_set_timeout(kTimeoutMs);

        // RM_TRIPLE_MODE_E 与当前三代控制器匹配；连接失败时立即返回，
        // 让调用方只输出本模块报告，不继续访问空句柄。
        const int init_result = rm_init(RM_TRIPLE_MODE_E);
        initialized_ = init_result == 0;
        if (suite_ != nullptr) {
            suite_->expect(initialized_, "rm_init", "error=" + std::to_string(init_result));
        }
        if (!initialized_) {
            return false;
        }

        handle_ = rm_create_robot_arm(kRobotIp, kRobotPort);
        const bool ok = handle_ != nullptr && handle_->id >= 0;
        if (suite_ != nullptr) {
            suite_->expect(ok, "rm_create_robot_arm", std::string(kRobotIp) + ":" + std::to_string(kRobotPort));
        }
        return ok;
    }

    rm_robot_handle *handle() const {
        return handle_;
    }

private:
    TestSuite *suite_ = nullptr;
    rm_robot_handle *handle_ = nullptr;
    bool initialized_ = false;
};

inline bool readRobotInfo(rm_robot_handle *handle, rm_robot_info_t *info, TestSuite *suite) {
    const int result = rm_get_robot_info(handle, info);
    const bool ok = result == 0;
    if (suite != nullptr) {
        suite->expect(ok, "rm_get_robot_info", "error=" + std::to_string(result));
        if (ok) {
            suite->info("arm_dof", std::to_string(static_cast<int>(info->arm_dof)));
            suite->info("force_type", forceTypeName(info->force_type));
        }
    }
    return ok;
}

inline bool validateDof(int dof, TestSuite *suite) {
    const bool valid = dof == kExpectedDof && dof > 0 && dof <= ARM_DOF;
    if (suite != nullptr) {
        suite->expect(valid, "expected_dof", "actual=" + std::to_string(dof));
    }
    return valid;
}

} // namespace realman_hw_test
