"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TdengineService = void 0;
const common_1 = require("@nestjs/common");
const rest_1 = require("@tdengine/rest");
const moment = require("moment");
let TdengineService = class TdengineService {
    constructor(config) {
        this.config = config;
        this.oldtime = 0;
        rest_1.options.path = this.config.path || '/rest/sql';
        rest_1.options.host = this.config.host;
        rest_1.options.port = this.config.port;
        rest_1.options.user = this.config.user;
        rest_1.options.passwd = this.config.passwd;
        this.databaseName = this.config.databaseName;
        this.dataPurgeInterval = this.config.dataPurgeInterval;
        this.connection = (0, rest_1.connect)(rest_1.options);
    }
    async createSTable(tableName) {
        const createDatabase = `CREATE DATABASE if not exists ${this.databaseName} BUFFER 32 DURATION 10 KEEP ${this.dataPurgeInterval} `;
        let createTableQuery = `CREATE STABLE if not exists ${this.databaseName}.${tableName} (time timestamp, data VARCHAR(32),flag INT) TAGS (iotId varchar(32), identifier varchar(32))`;
        let createLogTableQuery = `CREATE STABLE if not exists ${this.databaseName}.log${tableName} (time timestamp, infoType INT,commType INT,messageId varchar(32),content nchar(2048),_topic varchar(256) ) TAGS (iotId varchar(32))`;
        const cursor = this.connection.cursor();
        try {
            let res = await cursor.query(createDatabase);
            console.log("创建数据库:", this.databaseName);
            res = await cursor.query(createTableQuery);
            console.log("创建设备数据超级表:", `${this.databaseName}.${tableName}`);
            res = await cursor.query(createLogTableQuery);
            console.log("创建设备日志超级表:", `${this.databaseName}.log${tableName}`);
        }
        catch (err) {
            console.error("创建数据库失败 err=", err);
        }
    }
    async getTableSchema(tableName) {
        const schemaQuery = `DESCRIBE ${this.databaseName}.${tableName}`;
        const cursor = this.connection.cursor();
        return await cursor.query(schemaQuery);
    }
    async insertData(projectId, productId, iotId, identifier, time, data, flag) {
        const cursor = this.connection.cursor();
        const tableName = `st_${projectId}_${productId}`;
        let sqlCmd = "";
        if (identifier === null || identifier === "" || identifier === undefined) {
            if (this.oldtime !== 0 && time <= this.oldtime)
                time = this.oldtime + 1;
            sqlCmd = "INSERT INTO ";
            for (const key in data) {
                sqlCmd += `${this.databaseName}.${iotId}_${key} USING ${this.databaseName}.${tableName} TAGS ('${iotId}','${key}') VALUES (${time}, '${data[key]}',${flag}) `;
                time++;
            }
            this.oldtime = time;
        }
        else {
            sqlCmd = `INSERT INTO ${this.databaseName}.${iotId}_${identifier} USING ${this.databaseName}.${tableName} TAGS ('${iotId}','${identifier}') VALUES (${time}, '${data[identifier]}',${flag})`;
        }
        try {
            let res = await cursor.query(sqlCmd);
            if (res._code === 904 || res._code === 9731) {
                this.createSTable(tableName);
            }
            else if (res._code === 0) {
            }
        }
        catch (err) {
            console.error('[ERROR]', projectId, productId, iotId, 'inserting data:', err);
        }
    }
    async insertLog(projectId, productId, iotId, time, infoType, commType, messageId, content, topic) {
        const cursor = this.connection.cursor();
        const tableName = `st_${projectId}_${productId}`;
        content = content.replace(/\'/g, "\"");
        content = content.replace(/\"/g, "\\'");
        const sqlCmd = `INSERT INTO ${this.databaseName}.log${iotId} USING ${this.databaseName}.log${tableName} TAGS ('${iotId}') VALUES (${time}, ${infoType},${commType},'${messageId}','${content}','${topic}')`;
        try {
            let res = await cursor.query(sqlCmd);
            if (res._code === 904 || res._code === 9731) {
                this.createSTable(tableName);
            }
            else if (res._code === 0) {
            }
        }
        catch (err) {
            console.error('[ERROR]', projectId, productId, iotId, 'inserting data:', err);
        }
    }
    async queryLog(projectId, productId, iotId, startTime, endTime, pageSize, currentPage, asc, infoType, commType) {
        const tableName = `logst_${projectId}_${productId}`;
        let query = `SELECT * FROM ${this.databaseName}.log${iotId} WHERE time >= ${startTime} AND time <= ${endTime}`;
        let query1 = `SELECT COUNT(*) FROM ${this.databaseName}.log${iotId} WHERE time >= ${startTime} AND time <= ${endTime}`;
        if (!asc)
            asc = "DESC";
        if (Number(infoType) > -1 && Number(infoType) < 3) {
            query += ` AND infoType=${infoType}`;
            query1 += ` AND infoType=${infoType}`;
        }
        if (Number(commType) > -1 && Number(commType) < 2) {
            query += ` AND commType=${commType}`;
            query1 += ` AND commType=${commType}`;
        }
        if (currentPage && pageSize) {
            const offset = (currentPage - 1) * pageSize;
            query += ` ORDER BY time ${asc} LIMIT ${pageSize} OFFSET ${offset}`;
        }
        else {
            query += ` ORDER BY time ${asc}`;
        }
        const cursor = this.connection.cursor();
        const result = await cursor.query(query);
        const result1 = await cursor.query(query1);
        const totalCount = (result1._data && result1._data.length > 0)
            ? result1._data[0][0]
            : 0;
        const listData = result._data || [];
        return [totalCount, listData];
    }
    async delSTable(tableName) {
        const cursor = this.connection.cursor();
        try {
            let res = await cursor.query(`DROP STABLE ${this.databaseName}.${tableName}`);
            console.log("删除超级设备数据表完毕:", tableName);
            res = await cursor.query(`DROP STABLE ${this.databaseName}.log${tableName}`);
            console.log("删除超级设备日志表完毕:", tableName);
        }
        catch (err) {
            console.error("删除超级表失败", err);
        }
    }
    async queryData(projectId, productId, iotId, identifier, startTime, endTime, pageSize, currentPage, asc) {
        const tableName = `st_${projectId}_${productId}`;
        let query = `SELECT * FROM ${this.databaseName}.${iotId}_${identifier} WHERE time >= ${startTime} AND time <= ${endTime}`;
        let query1 = `SELECT COUNT(*) FROM ${this.databaseName}.${iotId}_${identifier} WHERE time >= ${startTime} AND time <= ${endTime}`;
        if (!asc)
            asc = "DESC";
        if (currentPage && pageSize) {
            const offset = (currentPage - 1) * pageSize;
            query += ` ORDER BY time ${asc} LIMIT ${pageSize} OFFSET ${offset}`;
        }
        else {
            query += ` ORDER BY time ${asc}`;
        }
        const cursor = this.connection.cursor();
        const result = await cursor.query(query);
        const result1 = await cursor.query(query1);
        return [result1._data[0][0], result._data];
    }
    async queryDataValues(projectId, productId, iotIds, identifier, startTime, endTime, pageSize, currentPage) {
        const tableName = `st_${projectId}_${productId}`;
        let query = `SELECT * FROM ${this.databaseName}.${tableName} WHERE time >= ${startTime} AND time <= ${endTime} AND iotId IN (${iotIds.map(id => `'${id}'`).join(', ')})  AND identifier = '${identifier}'`;
        if (currentPage && pageSize) {
            const offset = (currentPage - 1) * pageSize;
            query += ` ORDER BY time DESC LIMIT ${pageSize} OFFSET ${offset}`;
        }
        else {
            query += ` ORDER BY time DESC`;
        }
        const cursor = this.connection.cursor();
        let result = await cursor.query(query);
        return result._data;
    }
    async delTableData(projectId, productId, iotId, identifier, startTime, endTime) {
        const tableName = `st_${projectId}_${productId}`;
        const query = `DELETE FROM ${this.databaseName}.${tableName} WHERE iotid = '${iotId}' AND identifier = '${identifier}' AND time >= ${startTime} AND time <= ${endTime}`;
        const cursor = this.connection.cursor();
        try {
            const res = await cursor.query(query);
            console.log("数据删除成功", res);
        }
        catch (err) {
            console.error("数据删除失败", err);
        }
    }
    async getAllSTables() {
        try {
            const cursor = this.connection.cursor();
            const dbName = this.databaseName || 'yf_iot';
            const res = await cursor.query(`SHOW ${dbName}.STABLES`);
            if (res && res._code !== 0 && res._code !== undefined) {
                console.error(`TDengine 查询报错: 代码 ${res._code}, 描述: ${res._desc}`);
                return [];
            }
            const rawData = res?._data || (res?.result && res.result.data) || [];
            if (!Array.isArray(rawData)) {
                return [];
            }
            return rawData.map(row => row[0]);
        }
        catch (err) {
            console.error("获取超级表列表失败:", err.message);
            return [];
        }
    }
    async queryLogStatistics(projectId, statisticalType) {
        let intervalStr;
        let formatStr;
        let unit;
        let count;
        let sTime;
        let eTime;
        const now = moment();
        switch (statisticalType) {
            case 0:
                intervalStr = '1n';
                formatStr = 'YYYY-MM';
                unit = 'months';
                count = 12;
                sTime = now.clone().subtract(count - 1, 'months').startOf('month').valueOf();
                eTime = now.clone().endOf('month').valueOf();
                break;
            case 1:
                intervalStr = '1d';
                formatStr = 'YYYY-MM-DD';
                unit = 'days';
                count = 30;
                sTime = now.clone().subtract(count - 1, 'days').startOf('day').valueOf();
                eTime = now.clone().endOf('day').valueOf();
                break;
            case 2:
                intervalStr = '1h';
                formatStr = 'YYYY-MM-DD HH:00';
                unit = 'hours';
                count = 24;
                sTime = now.clone().subtract(count - 1, 'hours').startOf('hour').valueOf();
                eTime = now.clone().endOf('hour').valueOf();
                break;
            default:
                throw new Error("不支持的统计类型");
        }
        const allTables = await this.getAllSTables();
        const logPrefix = 'logst_'.toLowerCase();
        let targetTables = allTables.filter(t => t.toLowerCase().startsWith(logPrefix));
        if (projectId && projectId !== '') {
            const projectPrefix = `logst_${projectId}_`.toLowerCase();
            targetTables = targetTables.filter(t => t.toLowerCase().startsWith(projectPrefix));
        }
        if (targetTables.length === 0) {
            return { code: 200, message: "success", data: this.generateEmptyTimeline(sTime, eTime, formatStr, unit) };
        }
        const aggregationMap = new Map();
        const cursor = this.connection.cursor();
        for (const tableName of targetTables) {
            try {
                const query = `SELECT _wstart, COUNT(*) FROM ${this.databaseName}.${tableName} 
                           WHERE time >= ${sTime} AND time <= ${eTime} 
                           INTERVAL(${intervalStr})`;
                const res = await cursor.query(query);
                const rows = res?._data || res?.data || [];
                for (const row of rows) {
                    const rawTime = row[0];
                    const rowCount = Number(row[1]);
                    if (rawTime === undefined || rawTime === null)
                        continue;
                    const timeKey = moment(rawTime).format(formatStr);
                    const currentVal = aggregationMap.get(timeKey) || 0;
                    aggregationMap.set(timeKey, currentVal + rowCount);
                }
            }
            catch (err) {
                console.error(`[TDengine] 表 ${tableName} 查询失败:`, err.message);
            }
        }
        const finalResult = [];
        let cursorTime = moment(sTime);
        const endMoment = moment(eTime);
        while (cursorTime.isSameOrBefore(endMoment)) {
            const timeKey = cursorTime.format(formatStr);
            finalResult.push({
                time: timeKey,
                count: aggregationMap.get(timeKey) || 0
            });
            cursorTime.add(1, unit);
        }
        return {
            code: 200,
            message: "success",
            requestId: Date.now().toString(),
            data: finalResult
        };
    }
    generateEmptyTimeline(sTime, eTime, formatStr, unit) {
        const timeline = [];
        let cursorTime = moment(sTime);
        const endMoment = moment(eTime);
        while (cursorTime.isSameOrBefore(endMoment)) {
            timeline.push({
                time: cursorTime.format(formatStr),
                count: 0
            });
            cursorTime.add(1, unit);
        }
        return timeline;
    }
    async deleteDeviceSubTables(iotId) {
        const cursor = this.connection.cursor();
        const dbName = this.databaseName;
        const lowerIotId = iotId.toLowerCase();
        try {
            console.log(`[INFO] 正在从库 ${dbName} 查找前缀为 ${lowerIotId}_ 的子表...`);
            const searchSql = `SELECT table_name FROM information_schema.ins_tables 
                               WHERE db_name = '${dbName}' 
                               AND table_name LIKE '${lowerIotId}_%'`;
            const searchRes = await cursor.query(searchSql);
            const tables = searchRes?._data || (searchRes?.result && searchRes.result.data) || [];
            if (!Array.isArray(tables) || tables.length === 0) {
                console.log(`[INFO] 未找到设备 ${iotId} 的相关子表 (Result: 0)。`);
                return;
            }
            console.log(`[INFO] 找到 ${tables.length} 张子表，开始删除...`);
            let successCount = 0;
            const deletePromises = tables.map(async (row) => {
                const tableName = row[0];
                if (tableName) {
                    try {
                        await cursor.query(`DROP TABLE ${dbName}.${tableName}`);
                        successCount++;
                    }
                    catch (e) {
                        console.error(`[WARN] 删除 ${tableName} 失败:`, e.message);
                    }
                }
            });
            await Promise.all(deletePromises);
            console.log(`[SUCCESS] 设备 ${iotId} 清理完成，已删除 ${successCount} 张子表。`);
        }
        catch (err) {
            console.error('[ERROR] 批量删除设备子表失败:', err);
        }
    }
};
exports.TdengineService = TdengineService;
exports.TdengineService = TdengineService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, common_1.Inject)('TDENGINE_OPTIONS')),
    __metadata("design:paramtypes", [Object])
], TdengineService);
//# sourceMappingURL=tdengine.service.js.map