#pragma once

/*
 * pq_app.h
 *
 * 这是整个工具的公共边界文件：
 *   - interface.cpp 负责 Win32 GUI，通过这些结构体读取/显示配置和台账。
 *   - mq.cpp 负责 HTTP 台账/ICD 获取、RocketMQ 初始化和数据追踪落盘。
 *   - main.cpp 负责 JSON/XML 映射解析，生成 Excel 2003 XML 格式的 .xls。
 *   - icd_mapper.cpp 负责把外部 ICD 中的 DOI 描述补写到工作簿。
 *
 * 头文件中只放跨模块共享的数据结构和函数声明，不放 Win32/RocketMQ 的实现细节。
 */

#include <functional>
#include <string>
#include <utility>
#include <vector>

/*
 * 工具运行配置。
 *
 * 输入来源：
 *   1. pqDefaultConfig() 给出默认值；
 *   2. GUI 中 readConfig() 会用用户界面上的编辑框覆盖默认值；
 *   3. 命令行模式直接使用 main.cpp 中传入的 XML/JSON/origin 路径。
 *
 * 关键字段：
 *   frontInst/frontIp       前置节点标识和 IP，参与台账请求和追踪消息。
 *   terminalStatus          终端状态过滤条件，支持 JSON 数组字符串，如 "[0]"。
 *   monitorStatus           预留的测点状态过滤字段。
 *   icdFlag                 为 "1" 时按 devType 请求 ICD 列表，为其他值时请求全量/默认列表。
 *   dataStatTopic           统计数据 Topic，对应 frontType=cfg_stat_data。
 *   dataRealtimeTopic       实时数据 Topic，对应 frontType=cfg_3s_data。
 *   topicAsk/tagAsk/keyAsk  cfg_3s_data 发送 set_log 后的实时数据请求目标。
 *   dataTopic/dataTag/dataKey GUI 读取 frontType 后会设置成当前活跃数据 Topic/Tag/Key。
 *   traceRootDir            每次“数据追踪”按进程/装置/测点保存 JSON、origin 和报表的根目录。
 *
 * 输出影响：
 *   xmlSaveDir、jsonDataPath、originPaths、outputPath、traceRootDir 控制程序落盘位置。
 */
struct PqToolConfig {
    std::string frontInst = "6c2adb324940e19e208d717f4970f2ee";
    std::string frontIp = "192.168.1.68";
    std::string terminalStatus = "[0]";
    std::string monitorStatus = "[1,2]";
    std::string icdFlag = "0";

    std::string webDevice = "http://192.168.1.68:10202/nodeDevice/nodeDeviceList";
    std::string webIcd = "http://192.168.1.68:10202/icd/icdPathList";
    std::string webFiledownload = "http://192.168.1.68:10207/file/download";
    std::string xmlSaveDir = "runtime\\downloaded_xml";

    std::string producer = "TRACE_PRODUCER";
    std::string ipport = "192.168.1.68:9876";
    std::string producerAccessKey = "rmqroot";
    std::string producerSecretKey = "001@#njcnmq";
    std::string topicLOG = "ask_log_Topic";
    std::string tagLOG = "6c2adb324940e19e208d717f4970f2ee";
    std::string keyLOG = "Test_Keys";
    std::string topicAsk = "ask_data_Topic";
    std::string tagAsk = "6c2adb324940e19e208d717f4970f2ee";
    std::string keyAsk = "Test_Keys";

    std::string consumer = "TRACE_CONSUMER";
    std::string consumerIpport = "192.168.1.68:9876";
    std::string consumerAccessKey = "rmqroot";
    std::string consumerSecretKey = "001@#njcnmq";
    std::string dataStatTopic = "MQ_INST_1697676490574298_Bm3DLamM%LN_Topic";
    std::string dataRealtimeTopic = "MQ_INST_1697676490574298_Bm3DLamM%Real_Time_Data_Topic";
    std::string dataTopic = "MQ_INST_1697676490574298_Bm3DLamM%LN_Topic";
    std::string dataStatTag = "6c2adb324940e19e208d717f4970f2ee";
    std::string dataStatKey = "Test_Keys";
    std::string dataRealtimeTag = "6c2adb324940e19e208d717f4970f2ee";
    std::string dataRealtimeKey = "Test_Keys";
    std::string dataTag = "6c2adb324940e19e208d717f4970f2ee";
    std::string dataKey = "Test_Keys";
    std::string traceTopic = "Trace_Topic";
    std::string traceTag = "6c2adb324940e19e208d717f4970f2ee";
    std::string traceKey = "Test_Key";

    std::string xmlPath;
    std::string jsonDataPath = "runtime\\json\\jsondata.txt";
    std::vector<std::string> jsonDataPaths;
    std::vector<std::string> originPaths;
    std::string outputPath = "runtime\\final_sorted.xls";
    std::string traceRootDir = "runtime\\trace_data";
};

/*
 * 台账中的单个测点。
 *
 * 输入：pqFetchLedgerAndIcd() 从 WebDevice 接口的 monitorData 数组解析。
 * 输出：GUI 左侧树显示；数据追踪时 monitor.id 会写入 MQ 追踪消息。
 */
struct PqLedgerMonitor {
    std::string id;
    std::string name;
    std::string lineNo;
    std::string voltageLevel;
    std::string connectType;
    std::string status;
    std::vector<std::pair<std::string, std::string>> fields;
};

/*
 * 台账中的单个装置。
 *
 * 输入：pqFetchLedgerAndIcd() 从 WebDevice 接口的 data 数组解析。
 * 输出：
 *   - GUI 按 processNo -> device -> monitor 组织成树；
 *   - devType 用于匹配并下载对应 ICD/XML；
 *   - xmlPath 记录当前装置优先使用的 XML 模板。
 */
struct PqLedgerDevice {
    std::string id;
    std::string name;
    std::string ip;
    std::string status;
    std::string devType;
    std::string devKey;
    std::string orgName;
    std::string stationName;
    std::string subName;
    std::string manufacturer;
    std::string port;
    std::string series;
    std::string updateTime;
    std::string processNo;
    std::string maxProcessNum;
    std::string xmlPath;
    std::vector<std::pair<std::string, std::string>> fields;
    std::vector<PqLedgerMonitor> monitors;
};

// 日志回调：业务函数只发字符串，GUI/命令行决定显示到日志框还是控制台。
using PqLogFn = std::function<void(const std::string&)>;

/*
 * MQ/追踪过程中抛给 GUI 的事件。
 *
 * kind:
 *   sent  已构造并发送/落盘的数据追踪请求；
 *   data  DATATopic 收到的 jsondata 数据；
 *   trace TraceTopic 收到的 origin/原始 MMS 数据。
 *
 * path/traceDir 让 GUI 能把事件定位到具体追踪目录并自动刷新解析预览。
 */
struct PqMqEvent {
    std::string kind;
    std::string title;
    std::string body;
    std::string path;
    std::string monitorId;
    std::string traceDir;
};

using PqMqEventFn = std::function<void(const PqMqEvent&)>;

// 默认配置。mq.cpp 会把运行期路径修正到 exe 所在目录旁的 runtime 目录。
PqToolConfig pqDefaultConfig();

/*
 * 获取台账并下载 ICD/XML。
 *
 * 输入：cfg 中的 WebDevice/WebIcd/WebFiledownload、状态过滤和保存目录。
 * 输出：
 *   devices      装置和测点列表；
 *   firstXmlPath 第一个成功下载的 XML，用于自动填充 GUI 的 XML 路径；
 *   return       true 表示接口请求和解析流程完成，异常会抛出到调用方。
 */
bool pqFetchLedgerAndIcd(const PqToolConfig& cfg,
                         std::vector<PqLedgerDevice>& devices,
                         std::string& firstXmlPath,
                         PqLogFn log);

/*
 * 初始化 RocketMQ 运行时。
 *
 * 输入：生产者、消费者、Topic、Tag、Key、NameServer 和鉴权配置。
 * 输出：运行配置会保存到 runtime\json\mq_runtime_config.json；
 *       如果 DLL 不可用，函数仍返回 true，但后续发送会落到 mq_outbox_*.jsonl。
 */
bool pqInitializeMq(const PqToolConfig& cfg, PqLogFn log, PqMqEventFn event = {});

/*
 * 构造数据追踪消息体。
 *
 * 输入：
 *   device.processNo 统计数据追踪时参与 processNo；实时数据固定 processNo=0。
 *   monitor.id       写入 messageBody.id，代表追踪的测点。
 *   frontType        cfg_stat_data 或 cfg_3s_data。
 * 输出：外层 RocketMQ JSON 字符串，key 为 guid，messageBody 是内部 JSON 字符串。
 */
std::string pqBuildTracePayload(const PqToolConfig& cfg,
                                const PqLedgerDevice& device,
                                const PqLedgerMonitor& monitor,
                                const std::string& frontType);

/*
 * 发送一次数据追踪请求。
 *
 * 执行步骤：
 *   1. 构造 payload 并在 trace_context.json 中记录本次追踪上下文；
 *   2. 写 sent_trace_request.json 和 last_trace_request.json，便于复盘；
 *   3. RocketMQ 可用时先同步发送到 topicLOG/tagLOG/keyLOG；
 *   4. cfg_3s_data 再发送实时数据请求到 topicAsk/tagAsk/keyAsk；
 *   5. RocketMQ 不可用时追加到对应 Topic 的 mq_outbox_*.jsonl 作为兜底。
 */
bool pqSendTraceRequest(const PqToolConfig& cfg,
                        const PqLedgerDevice& device,
                        const PqLedgerMonitor& monitor,
                        const std::string& frontType,
                        PqLogFn log,
                        PqMqEventFn event = {});

// 清空某个追踪目录下的 txt/json/jsonl/log 文本记录，保留目录本身。
void pqClearTraceRecords(const PqToolConfig& cfg,
                         const PqLedgerDevice& device,
                         const PqLedgerMonitor& monitor);

// 按 traceRootDir/process_x/device_x/monitor_x 生成稳定追踪目录。
std::string pqTraceDirectoryFor(const PqToolConfig& cfg,
                                const PqLedgerDevice& device,
                                const PqLedgerMonitor& monitor);

// 为装置寻找 XML：优先 device.xmlPath，其次 icd_response.json 下载缓存，最后 cfg.xmlPath。
std::string pqFindDeviceXmlPath(const PqToolConfig& cfg, const PqLedgerDevice& device);

/*
 * 解析 XML + 单个 jsondata + 多个 origin，生成 Excel XML 工作簿。
 * 这是旧调用形式，内部会转成 vector 版本。
 */
bool pqRunParser(const std::string& xmlPath,
                 const std::string& jsonPath,
                 const std::vector<std::string>& originPaths,
                 const std::string& outPath,
                 std::string* err,
                 const std::string& frontType = "");

/*
 * 解析 XML + 多个 jsondata + 多个 origin，生成 Excel XML 工作簿。
 *
 * 输入：
 *   xmlPath      ICD/XML 映射模板；
 *   jsonPaths    DATATopic 统计/实时数据，可按 DATA_TYPE 分桶；
 *   originPaths  TraceTopic 原始 MMS 数据，用于值对比和未匹配列表；
 *   frontType    为空表示生成所有工作表；cfg_stat_data/cfg_3s_data 表示只生成对应流程。
 * 输出：
 *   outPath      Excel 2003 XML 格式的 .xls；
 *   err          失败时写入异常信息；
 *   return       true 成功，false 失败。
 */
bool pqRunParser(const std::string& xmlPath,
                 const std::vector<std::string>& jsonPaths,
                 const std::vector<std::string>& originPaths,
                 const std::string& outPath,
                 std::string* err,
                 const std::string& frontType = "");

/*
 * 把外部 ICD 中 LN/DOI 的 desc 导入到工作簿“未匹配的原始数据”页。
 * 通过原始路径前缀匹配 DO 路径，帮助解释没有在当前映射中匹配到的 origin 数据。
 */
bool pqApplyIcdDescriptionsToWorkbook(const std::string& icdPath,
                                      const std::string& workbookPath,
                                      std::string* message = nullptr);

// GUI 入口。main.cpp 无命令行参数时调用，内部创建 Win32 主窗口和消息循环。
int runPqGuiApp();
