"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SocketClient = exports.Des3Util = exports.Response = exports.AggregateResData = exports.SendData = void 0;
const net = require("net");
const events_1 = require("events");
const crypto = require("crypto");
const dayjs = require("dayjs");
class SendData {
}
exports.SendData = SendData;
class AggregateResData {
}
exports.AggregateResData = AggregateResData;
class Response {
}
exports.Response = Response;
/**
 * 3DES加解密
 */
class Des3Util {
    /**
     * 加密
     */
    static encrypt(data, key, iv) {
        const cipher = crypto.createCipheriv("des-ede3-cbc", key, iv);
        let encrypted = null;
        if (typeof data === "string") {
            encrypted = cipher.update(data, "utf8", "base64");
        }
        else {
            encrypted = cipher.update(JSON.stringify(data), "utf8", "base64");
        }
        encrypted += cipher.final("base64");
        return encrypted;
    }
    /**
     * 解密
     */
    static decrypt(data, key, iv) {
        const decipher = crypto.createDecipheriv("des-ede3-cbc", key, iv);
        let decrypted = decipher.update(data, "base64", "utf8");
        decrypted += decipher.final("utf8");
        return decrypted;
    }
}
exports.Des3Util = Des3Util;
/**
 * 澳门通SOCKET客户端
 *
 * 珠海曜麒支付方案(停車場系統)V1.20.docx
 */
class SocketClient {
    get socketInstance() {
        return this._socket;
    }
    constructor(opts) {
        this._socket = null;
        this.opts = null;
        this._disposed = null;
        this.timeout_timers = new Map();
        this.opts = Object.assign({}, opts);
        this.opts.host = this.opts.host || this.opts.ip;
        this.opts.port = this.opts.port || 8041;
        this._emitter = new events_1.EventEmitter();
        this._connect();
    }
    on(eventName, listener) {
        if (this._disposed)
            return;
        this._emitter.on(eventName, listener);
        return this;
    }
    off(eventName, listener) {
        if (this._disposed)
            return;
        this._emitter.off(eventName, listener);
        return this;
    }
    /**
     * 发送数据
     */
    send(data) {
        if (!this._socket || !this._socket.writable || this._socket.destroyed) {
            return false;
        }
        this._add_timeout_auto_res_timer(data);
        let enc = Des3Util.encrypt(data, this.opts.appSecret, dayjs().format('YYYYMMDD'));
        this._socket.write(Buffer.from(enc));
        return true;
    }
    /**
     * 添加超时自动响应定时器
     * @param data
     * @returns
     */
    _add_timeout_auto_res_timer(data) {
        if (!data || !data.PayOutTransNo || typeof data.PayOutTransNo !== 'string' || !this.opts.timeoutAutoRes)
            return;
        let key = data.PayOutTransNo;
        let timer = this.timeout_timers.get(key);
        if (timer) {
            clearTimeout(timer);
            this.timeout_timers.delete(key);
        }
        timer = setTimeout(() => {
            this.timeout_timers.delete(key);
            let timeout_res = {
                ResCode: -1,
                ResMsg: '響應超時（AutoRes）',
                ResData: JSON.stringify(data),
                ResCount: 0
            };
            this._emitter.emit("response", timeout_res);
        }, this.opts.timeoutMs || 1000 * 12);
        this.timeout_timers.set(key, timer);
    }
    _del_timeout_res_timer(res) {
        if (!res.ResData || typeof res.ResData !== 'string')
            return;
        try {
            let data = JSON.parse(res.ResData);
            let key = data === null || data === void 0 ? void 0 : data.PayOutTransNo;
            if (!key || typeof key !== 'string')
                return;
            let timer = this.timeout_timers.get(key);
            if (timer) {
                clearTimeout(timer);
                this.timeout_timers.delete(key);
            }
        }
        catch (error) {
            this._emitter.emit("error", error);
        }
    }
    destroy() {
        this._dispose();
    }
    _connect() {
        if (this._disposed)
            return;
        if (this._socket) {
            this._socket.removeAllListeners();
            this._socket.destroy();
            this._socket = null;
        }
        let socket = net.connect(this.opts);
        socket.on("connect", () => {
            this._emitter.emit("connect");
        });
        socket.on("data", (data) => {
            this._onData(data);
            this._emitter.emit("data", data);
        });
        socket.on("error", (err) => {
            this._emitter.emit("error", err);
        });
        socket.on("close", (hadError) => __awaiter(this, void 0, void 0, function* () {
            this._onClose();
            this._emitter.emit("close", hadError);
        }));
        this._socket = socket;
    }
    _onData(data) {
        if (!data || data.length === 0)
            return;
        try {
            let json = data.toString('utf8');
            let res = JSON.parse(json);
            this._del_timeout_res_timer(res);
            //忽略D2响应
            //TODO:怎么判断是扣费响应
            // if (this.opts.ignorePaymentD2Res && obj.CardStatus === 'D2') return;
            this._emitter.emit("response", res);
        }
        catch (error) {
            this._emitter.emit("decodeError", error);
        }
    }
    _onClose() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this._disposed) {
                yield new Promise((resolve) => {
                    setTimeout(resolve, this.opts.reconnectDelay || 1000 * 10);
                });
                this._connect();
            }
        });
    }
    _dispose() {
        if (this._disposed)
            return;
        this._disposed = true;
        if (this._socket) {
            if (!this._socket.destroyed) {
                this._emitter.emit("close", false);
            }
            this._socket.removeAllListeners();
            this._socket.destroy();
            this._socket = null;
        }
        this._emitter.removeAllListeners();
        this._emitter = null;
    }
}
exports.SocketClient = SocketClient;
