"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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const es6_promisify_1 = require("es6-promisify");
const events_1 = __importDefault(require("events"));
const fs_1 = __importDefault(require("fs"));
const jsonfile_1 = __importDefault(require("jsonfile"));
const lodash_1 = require("lodash");
const node_fetch_1 = __importDefault(require("node-fetch"));
const path_1 = __importDefault(require("path"));
const typedi_1 = require("typedi");
const appInfo_1 = require("./appInfo");
const ipReg = /((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))/;
const jsonWriteFile = es6_promisify_1.promisify(jsonfile_1.default.writeFile);
const fsUnlink = es6_promisify_1.promisify(fs_1.default.unlink);
const fsExists = (p) => new Promise(resolve => {
    fs_1.default.exists(p, exists => {
        resolve(exists);
    });
});
/**
 * Created by tsxuehu on 8/3/17.
 */
let HostService = class HostService extends events_1.default {
    constructor(appInfoService) {
        super();
        // userId -> { filename -> content}
        this.userHostFilesMap = {};
        // 缓存
        // userId, {globHostMap, hostMap}
        this.inUsingHostsMapCache = {};
        const proxyDataDir = appInfoService.getProxyDataDir();
        this.hostSaveDir = path_1.default.join(proxyDataDir, 'host');
        const contentMap = fs_1.default
            .readdirSync(this.hostSaveDir)
            .filter(name => name.endsWith('.json'))
            .reduce((prev, curr) => {
            try {
                prev[curr] = jsonfile_1.default.readFileSync(path_1.default.join(this.hostSaveDir, curr));
            }
            catch (e) {
                // ignore
            }
            return prev;
        }, {});
        lodash_1.forEach(contentMap, (content, fileName) => {
            const hostName = content.name;
            const userId = fileName.substr(0, this._getUserIdLength(fileName, hostName));
            this.userHostFilesMap[userId] = this.userHostFilesMap[userId] || {};
            this.userHostFilesMap[userId][hostName] = content;
        });
    }
    resolveHost(userId, hostname) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!hostname) {
                return hostname;
            }
            if (ipReg.test(hostname)) {
                return hostname;
            }
            let ip;
            const inUsingHosts = this.getInUsingHosts(userId);
            ip = inUsingHosts.hostMap[hostname];
            if (ip) {
                return ip;
            }
            // 配置 *开头的host  计算属性globHostMap已经将*去除
            ip = lodash_1.find(inUsingHosts.globHostMap, (_, host) => {
                return hostname.endsWith(host);
            });
            if (ip) {
                return ip;
            }
            return hostname;
        });
    }
    /**
     * 获取用户的host文件列表
     * @param userId
     * @returns {Array}
     */
    getHostFileList(userId) {
        const fileList = [];
        lodash_1.forEach(this.userHostFilesMap[userId], content => {
            fileList.push({
                checked: content.checked,
                description: content.description,
                meta: content.meta,
                name: content.name,
            });
        });
        return fileList;
    }
    /**
     * 创建host文件
     * @param userId
     * @param name
     * @param description
     * @returns {boolean}
     */
    createHostFile(userId, name, description) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.userHostFilesMap[userId] && this.userHostFilesMap[userId][name]) {
                // 文件已经存在不让创建
                return false;
            }
            const content = {
                checked: false,
                content: {},
                description,
                meta: {
                    local: true,
                },
                name,
            };
            this.userHostFilesMap[userId] = this.userHostFilesMap[userId] || {};
            this.userHostFilesMap[userId][name] = content;
            const hostfileName = this._getHostFilePath(userId, name);
            yield jsonWriteFile(hostfileName, content, { encoding: 'utf-8' });
            this.emit('data-change', userId, this.getHostFileList(userId));
            this.emit('host-saved', userId, name, content);
            return true;
        });
    }
    deleteHostFile(userId, name) {
        return __awaiter(this, void 0, void 0, function* () {
            delete this.userHostFilesMap[userId][name];
            delete this.inUsingHostsMapCache[userId];
            /**
             * 删除文件
             */
            const filePath = this._getHostFilePath(userId, name);
            const exists = yield fsExists(filePath);
            if (exists) {
                yield fsUnlink(filePath);
            }
            this.emit('data-change', userId, this.getHostFileList(userId));
            this.emit('host-deleted', userId, name);
        });
    }
    toggleUseHost(userId, filename) {
        return __awaiter(this, void 0, void 0, function* () {
            const toSaveFileName = [];
            lodash_1.forEach(this.userHostFilesMap[userId], (content, name) => {
                if (content.name === filename) {
                    content.checked = !content.checked;
                    toSaveFileName.push(name);
                }
            });
            // 保存文件
            for (const name of toSaveFileName) {
                const hostfileName = this._getHostFilePath(userId, name);
                const content = this.userHostFilesMap[userId][name];
                yield jsonWriteFile(hostfileName, content, { encoding: 'utf-8' });
            }
            delete this.inUsingHostsMapCache[userId];
            this.emit('data-change', userId, this.getHostFileList(userId));
        });
    }
    getHostFile(userId, name) {
        if (!this.userHostFilesMap[userId]) {
            return {};
        }
        return this.userHostFilesMap[userId][name];
    }
    saveHostFile(userId, name, content) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.userHostFilesMap[userId]) {
                this.userHostFilesMap[userId] = {};
            }
            this.userHostFilesMap[userId][name] = content;
            // 如果正在使用，则删除
            if (content.checked) {
                delete this.inUsingHostsMapCache[userId];
            }
            const hostfileName = this._getHostFilePath(userId, name);
            yield jsonWriteFile(hostfileName, content, { encoding: 'utf-8' });
            this.emit('host-saved', userId, name, content);
            this.emit('data-change', userId, this.getHostFileList(userId));
        });
    }
    importRemoteHostFile(userId, url) {
        return __awaiter(this, void 0, void 0, function* () {
            const resp = yield node_fetch_1.default(url);
            const f = yield resp.json();
            f.meta = {
                local: false,
                url,
            };
            if (!f.content) {
                f.content = {};
            }
            if (!f.name) {
                f.name = url.split('/').slice(-1)[0] || url;
            }
            if (this.getHostFile(userId, f.name) &&
                this.getHostFile(userId, f.name).checked) {
                f.checked = true;
            }
            else {
                f.checked = false;
            }
            return yield this.saveHostFile(userId, f.name, f);
        });
    }
    _getHostFilePath(userId, hostName) {
        const fileName = `${userId}_${hostName}.json`;
        const filePath = path_1.default.join(this.hostSaveDir, fileName);
        return filePath;
    }
    _getUserIdLength(ruleFileName, hostName) {
        return ruleFileName.length - hostName.length - 6;
    }
    /**
     * 获取用户生效的host
     * @param userId
     * @returns {*}
     */
    getInUsingHosts(userId) {
        let hosts = this.inUsingHostsMapCache[userId];
        if (!hosts) {
            // 读文件加载host
            const hostMap = {};
            const globHostMap = {};
            const userHostFile = this.userHostFilesMap[userId] || {};
            Object.keys(userHostFile).forEach(name => {
                const file = this.userHostFilesMap[userId][name];
                if (!file.checked) {
                    return;
                }
                lodash_1.forEach(file.content, (ip, host) => {
                    if (host.startsWith('*')) {
                        globHostMap[host.substr(1, host.length)] = ip;
                    }
                    else {
                        hostMap[host] = ip;
                    }
                });
            });
            hosts = {
                globHostMap,
                hostMap,
            };
            this.inUsingHostsMapCache[userId] = hosts;
        }
        return hosts;
    }
};
HostService = __decorate([
    typedi_1.Service(),
    __metadata("design:paramtypes", [appInfo_1.AppInfoService])
], HostService);
exports.HostService = HostService;
//# sourceMappingURL=host.js.map