// HjyIot.h - 贺简云物联 IoT 设备接入库（ESP32 Arduino C++）
//
// 对应协议文档：docs/设备接入与公开应用.md（HTTP 设备接入 + 云盘 + report.fs + OTA）
// 依赖：Arduino core for ESP32 内置 WiFi / HTTPClient / Update（无需第三方 JSON 库；
//       示例用到 ArduinoJson，仅为演示解析服务器 JSON 文本）
//
// 基本用法：
//   HjyIotDevice dev("https://www.hjyiot.cn/api.php", "<device_id>", "<device_token>");
//   String resp;
//   dev.heartbeat(resp);                       // 心跳
//   dev.report("{\"temperature\":27.5}", resp);// 属性上报
//   dev.pullCommands(resp);                    // 拉取指令
//
// 说明：
//   1. 设备令牌在平台管理后台「设备管理 → 设备详情」生成/重置；
//   2. 本库返回服务器原始 JSON 文本，业务侧可自行解析（或搭配 ArduinoJson 使用）；
//   3. 云盘写类指令（report.fs）要求租户已在 Web 端签署《云盘使用协议》，未开通会被拒绝；
//   4. OTA 固件下载（otaDownload）成功返回二进制流而非 JSON，本库直接流式写入
//      OTA 分区并边写边算 SHA-256，与响应头 X-OTA-SHA256 比对一致后才落分区；
//   5. 生产环境建议使用 https 并在 request 内替换 setInsecure 为固定 CA 校验。

#ifndef HJY_IOT_H
#define HJY_IOT_H

#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>

class HjyIotDevice {
public:
    /// baseUrl 形如 https://www.hjyiot.cn/api.php（不含 query）
    HjyIotDevice(const char* baseUrl, const char* deviceId, const char* deviceToken);
    ~HjyIotDevice();

    /// 心跳 / 上线（GET/POST 均可，本库使用 POST）
    bool heartbeat(String& out, unsigned long timeoutMs = 8000);

    /// 属性上报。propsJson 为 JSON 对象文本，如 {"temperature":27.5,"humidity":52}
    bool report(const String& propsJson, String& out, unsigned long timeoutMs = 8000);

    /// 属性上报 + 云盘内容维护指令。fsJsonArray 为 JSON 数组文本，如
    /// [{"op":"write","path":"logs/2026-09-08.txt","content":"x","mode":"append","create":true}]
    bool reportWithFs(const String& propsJson, const String& fsJsonArray,
                      String& out, unsigned long timeoutMs = 8000);

    /// 拉取待执行指令（取走即 sent，执行结果需 report 回写）
    bool pullCommands(String& out, unsigned long timeoutMs = 8000);

    /// 云盘空间概览（只读）
    bool diskStats(String& out, unsigned long timeoutMs = 8000);

    /// 云盘目录浏览（只读）；path 为空串表示根目录
    bool diskList(const String& path, String& out, unsigned long timeoutMs = 8000);

    /// 云盘文件读取（只读；txt 返回 content，jpg 返回 base64 data）
    bool diskRead(const String& path, String& out, unsigned long timeoutMs = 8000);

    /// OTA 固件下载：流式写入 OTA 分区（含 SHA-256 校验，校验通过才 Update.end）
    ///
    /// jobId 来自平台指令 type=ota 的 payload.job_id；调用前无需自行 GET 请求。
    /// 成功返回 true：version / size / sha256Hex 为固件信息（sha256Hex 为小写十六进制），
    /// 固件已写入 OTA 分区，调用方紧接 ESP.restart() 即生效。
    /// 失败返回 false：err 为平台返回的 JSON 错误包（code=1002/1003/1004/1006）或本地原因
    /// （分区空间不足、下载中断、SHA-256 不匹配等）；失败时 OTA 分区不会被切换。
    /// 注意：下载窗口为推送后 24 小时，过期需在平台重新推送。
    bool otaDownload(const String& jobId, String& version, size_t& size,
                     String& sha256Hex, String& err, unsigned long timeoutMs = 60000);

    /// 底层通用请求：method GET/POST；body 为空表示无请求体
    bool request(const String& method, const String& action, const String& body,
                 String& out, unsigned long timeoutMs = 8000);

    /// 工具：生成 Basic 鉴权头（Authorization: Basic ...）
    static String buildAuthHeader(const char* deviceId, const char* deviceToken);

private:
    String _baseUrl;
    String _deviceId;
    String _deviceToken;
};

#endif // HJY_IOT_H
