"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 MagicResourceService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MagicResourceService = void 0;
const core_1 = require("@midwayjs/core");
const http_1 = require("@midwayjs/core/dist/error/http");
const decorator_1 = require("@midwayjs/decorator");
const compressing_1 = require("compressing");
const dayjs = require("dayjs");
const fse = require("fs-extra");
const os = require("os");
const path = require("path");
const recursive_readdir_async_1 = require("recursive-readdir-async");
const magic_provide_1 = require("../../magic.provide");
const modules_1 = require("../../modules");
const utils_1 = require("../../utils");
class MagicResourceFileStore {
    constructor(location) {
        this.type = 'file';
        this.prefix = '/*================================\n';
        this.separator = '\n================================*/\n';
        this.resources = [];
        this.basePath = location;
    }
    async listFiles() {
        if (!(await fse.pathExists(this.basePath))) {
            fse.mkdirsSync(this.basePath);
        }
        return ((await (0, recursive_readdir_async_1.list)(this.basePath)) || []).filter(f => !f.isDirectory);
    }
    /**
     * 读取所有文件缓存到内存
     */
    async reloadResources() {
        const files = await this.listFiles();
        const result = [];
        for (const file of files) {
            if (file.name.endsWith('.json')) {
                const folderJSON = (await fse.readJSON(file.fullname));
                result.push({
                    filePath: file.fullname,
                    resource: folderJSON,
                });
            }
            else if (file.name.endsWith('.js')) {
                const content = (await fse.readFile(file.fullname)).toString();
                const strings = content.replace(this.prefix, '').split(this.separator);
                const fileJSON = JSON.parse(strings[0]);
                fileJSON.script = strings[1];
                result.push({
                    filePath: file.fullname,
                    resource: fileJSON,
                });
            }
        }
        this.resources = result;
    }
    /**
     * 文件参数匹配
     * @param condition 查询条件
     */
    async select(condition = {}) {
        const keys = Object.keys(condition);
        const result = [];
        for (const { filePath, resource } of this.resources) {
            let isMatchCount = 0;
            for (const key of keys) {
                if (resource[key] == condition[key]) {
                    isMatchCount++;
                }
            }
            if (isMatchCount === keys.length) {
                result.push({
                    filePath,
                    resource: { ...resource },
                });
            }
        }
        return result.sort((a, b) => {
            if (dayjs(a.resource.createdAt).isBefore(dayjs(b.resource.createdAt))) {
                return -1;
            }
            return 0;
        });
    }
    /**
     * 文件参数匹配 返回一个
     * @param condition 查询条件
     */
    async selectOne(condition) {
        const result = await this.select(condition);
        return result[0];
    }
    async deleteById(id) {
        const { resource, filePath } = await this.selectOne({ id });
        if (resource != null) {
            if (resource.isFolder) {
                const resources = await this.findByGroupId(resource.type, resource.id);
                if (resources.length > 0) {
                    throw new core_1.MidwayCommonError('子节点不为空');
                }
                if (filePath) {
                    await fse.rm(filePath);
                    await fse.rmdir(path.join(filePath, '..'));
                    const index = this.resources.findIndex(r => r.resource.id === id);
                    if (index != -1) {
                        this.resources.splice(index, 1);
                    }
                    return;
                }
            }
            else {
                if (filePath) {
                    await fse.rm(filePath);
                    const index = this.resources.findIndex(r => r.resource.id === id);
                    if (index != -1) {
                        this.resources.splice(index, 1);
                    }
                    return;
                }
            }
        }
        throw new core_1.MidwayCommonError('此ID的数据已不存在');
    }
    async findByGroupId(type, groupId, isFolder) {
        return (await this.select({ type, groupId, isFolder })).map(r => r.resource);
    }
    async findById(id) {
        var _a;
        return (_a = (await this.selectOne({ id }))) === null || _a === void 0 ? void 0 : _a.resource;
    }
    async findByType(type, enabled = true, isFolder = false) {
        return (await this.select({ type, enabled, isFolder })).map(r => r.resource);
    }
    async lock(id, fileLock) {
        const resource = await this.findById(id);
        if (resource != null) {
            resource.fileLock = fileLock;
            return this.saveOrUpdate(resource);
        }
        throw new core_1.MidwayCommonError('此ID的数据已不存在');
    }
    /**
     * 存储文件
     * @param resource 资源对象
     * @param isUpdate 是否更新
     */
    async saveFile(resource, isUpdate = false) {
        var _a;
        const { id, isFolder } = resource;
        if (isFolder) {
            const newFolderPath = path.join(this.basePath, resource.type, resource.fileName);
            if (isUpdate) {
                const oldResource = (_a = (await this.selectOne({ id }))) === null || _a === void 0 ? void 0 : _a.resource;
                if (oldResource != null) {
                    const oldFolderPath = path.join(this.basePath, oldResource.type, oldResource.fileName);
                    if (oldFolderPath !== newFolderPath) {
                        await fse.rename(oldFolderPath, newFolderPath);
                    }
                }
            }
            if (!(await fse.pathExists(newFolderPath))) {
                fse.mkdirsSync(newFolderPath);
            }
            const folderJSONPath = path.join(newFolderPath, 'group.json');
            await fse.writeJSON(folderJSONPath, resource, {
                spaces: 2,
            });
            if (isUpdate) {
                const index = this.resources.findIndex(r => r.resource.id === id);
                if (index != -1) {
                    this.resources[index] = {
                        filePath: folderJSONPath,
                        resource,
                    };
                }
                else {
                    this.resources.unshift({
                        filePath: folderJSONPath,
                        resource,
                    });
                }
            }
            else {
                this.resources.unshift({
                    filePath: folderJSONPath,
                    resource,
                });
            }
        }
        else {
            const folderPath = path.join(this.basePath, resource.type);
            if (!(await fse.pathExists(folderPath))) {
                fse.mkdirsSync(folderPath);
            }
            const mappingName = await this.getMappingName(resource);
            const fileName = mappingName.substring(0, mappingName.lastIndexOf(resource.fileName)) +
                `${id}.js`;
            const filePath = path.join(folderPath, fileName);
            const newResource = { ...resource };
            delete newResource.script;
            const content = `${this.prefix}${JSON.stringify(newResource, null, 2)}${this.separator}${resource.script || ''}`;
            await fse.writeFile(filePath, content);
            if (isUpdate) {
                const index = this.resources.findIndex(r => r.resource.id === id);
                if (index != -1) {
                    this.resources[index] = {
                        filePath,
                        resource,
                    };
                }
                else {
                    this.resources.push({
                        filePath,
                        resource,
                    });
                }
            }
            else {
                this.resources.push({
                    filePath,
                    resource,
                });
            }
        }
    }
    async saveOrUpdate(resource) {
        let { id, type, enabled, isFolder, ...other } = resource;
        const isUpdate = id != null;
        const result = await this.selectOne({
            method: other.method,
            path: other.path,
            type,
            isFolder,
        });
        const oldResource = result === null || result === void 0 ? void 0 : result.resource;
        if (oldResource != null && id !== oldResource.id) {
            throw new http_1.BadRequestError('资源重复');
        }
        if (isUpdate) {
            resource.updatedAt = dayjs().format('YYYY-MM-DD HH:mm:ss');
        }
        else {
            resource.createdAt = dayjs().format('YYYY-MM-DD HH:mm:ss');
            id = (0, utils_1.randomUUID)();
            resource.id = id;
        }
        await this.saveFile(resource, isUpdate);
        if (isFolder) {
            if (isUpdate) {
                await magic_provide_1.MagicProvideContainer.reInitByType(type);
            }
        }
        else {
            if (enabled) {
                await (await magic_provide_1.MagicProvideContainer.getMagicProvide(type)).add(resource);
            }
            else {
                await (await magic_provide_1.MagicProvideContainer.getMagicProvide(type)).removeById(id);
            }
        }
        return resource;
    }
    async getMappingPath(resource) {
        if (resource.groupId != '0') {
            const groupResource = await this.findById(resource.groupId);
            return (await this.getMappingPath(groupResource)) + resource.path;
        }
        return resource.path;
    }
    async getMappingName(resource) {
        if (resource.groupId != '0') {
            const groupResource = await this.findById(resource.groupId);
            return ((await this.getMappingName(groupResource)) + '/' + resource.fileName);
        }
        return resource.fileName;
    }
    async getTreeByType(type, groupId = '0') {
        const resources = (await this.select({
            type,
            groupId,
        })).map(r => r.resource);
        resources.forEach(r => {
            delete r.definition;
            delete r.script;
        });
        for (const resource of resources) {
            resource.children = await this.getTreeByType(type, resource.id);
        }
        return resources;
    }
    async exportResource(ids) {
        const result = [];
        const resources = (await Promise.all([...ids.map(id => this.findById(id))])).filter(r => r != null);
        result.push(...resources);
        for (const { groupId } of resources) {
            const index = result.findIndex(r => r.id === groupId);
            if (index != -1) {
                result.splice(index, 1);
            }
            const groupResource = await this.findById(groupId);
            if (groupResource != null) {
                result.unshift(groupResource);
            }
        }
        return result;
    }
    async importResource(resources, mode) {
        for (const resource of resources) {
            await this.saveFile(resource);
        }
    }
    async deleteAll() {
        await fse.rm(this.basePath, { recursive: true, force: true });
    }
}
class MagicResourceDatabaseStore {
    constructor({ sqlModule, config }) {
        this.type = 'database';
        this.config = config;
        this.sqlModule = sqlModule;
    }
    async deleteById(id) {
        const resource = await this.findById(id);
        if (resource != null) {
            if (resource.isFolder) {
                const resources = await this.findByGroupId(resource.type, resource.id);
                if (resources.length > 0) {
                    throw new core_1.MidwayCommonError('子节点不为空');
                }
            }
            return await this.sqlModule
                .table(this.config.resource.tableName)
                .where()
                .eq('id', id)
                .delete();
        }
        throw new core_1.MidwayCommonError('此ID的数据已不存在');
    }
    async findByGroupId(type, groupId, isFolder = false) {
        return (await this.sqlModule
            .table(this.config.resource.tableName)
            .where()
            .eq('type', type)
            .eq('groupId', groupId)
            .eq('isFolder', isFolder)
            .select()).filter(r => {
            r.definition = JSON.parse(r.definition || '{}');
            return r;
        });
    }
    async findById(id) {
        const one = await this.sqlModule
            .table(this.config.resource.tableName)
            .where()
            .eq('id', id)
            .selectOne();
        if (one) {
            one.definition = JSON.parse(one.definition || '{}');
            return one;
        }
        return undefined;
    }
    async findByType(type, enabled = true, isFolder = false) {
        return (await this.sqlModule
            .table(this.config.resource.tableName)
            .where()
            .eq('type', type)
            .eq('enabled', enabled)
            .eq('isFolder', isFolder)
            .select()).map(r => {
            r.definition = JSON.parse(r.definition || '{}');
            return r;
        });
    }
    async lock(id, fileLock) {
        return this.sqlModule
            .table(this.config.resource.tableName)
            .primary('id')
            .update({
            id,
            fileLock,
        });
    }
    async saveOrUpdate(resource) {
        let { id, type, isFolder, enabled, groupId, ...other } = resource;
        const isUpdate = id != null;
        const oldResource = (await this.sqlModule
            .table(this.config.resource.tableName)
            .where()
            .eq('method', other.method)
            .eq('path', other.path)
            .eq('type', type)
            .eq('isFolder', isFolder)
            .eq('groupId', groupId)
            .selectOne());
        if (oldResource != null && id !== oldResource.id) {
            throw new http_1.BadRequestError('资源重复');
        }
        const definition = resource.definition;
        resource.definition = JSON.stringify(resource.definition);
        if (isUpdate) {
            resource.updatedAt = dayjs().format('YYYY-MM-DD HH:mm:ss');
        }
        else {
            resource.createdAt = dayjs().format('YYYY-MM-DD HH:mm:ss');
        }
        id = await this.sqlModule
            .table(this.config.resource.tableName)
            .primary('id', () => (0, utils_1.randomUUID)())
            .save(resource);
        resource.definition = definition;
        if (!isUpdate) {
            resource.id = id;
        }
        if (isFolder) {
            if (isUpdate) {
                await magic_provide_1.MagicProvideContainer.reInitByType(type);
            }
        }
        else {
            if (enabled) {
                await (await magic_provide_1.MagicProvideContainer.getMagicProvide(type)).add(resource);
            }
            else {
                await (await magic_provide_1.MagicProvideContainer.getMagicProvide(type)).removeById(id);
            }
        }
        return resource;
    }
    async getTreeByType(type, groupId = '0') {
        const resources = (await this.sqlModule
            .table(this.config.resource.tableName)
            .excludes('definition', 'script')
            .where()
            .eq('type', type)
            .eq('groupId', groupId)
            .orderBy('createdAt')
            .select());
        for (const resource of resources) {
            resource.children = await this.getTreeByType(type, resource.id);
        }
        return resources;
    }
    async getMappingPath(resource) {
        if (resource.groupId != '0') {
            const groupResource = await this.findById(resource.groupId);
            return (await this.getMappingPath(groupResource)) + resource.path;
        }
        return resource.path;
    }
    async getMappingName(resource) {
        if (resource.groupId != '0') {
            const groupResource = await this.findById(resource.groupId);
            return ((await this.getMappingPath(groupResource)) + '/' + resource.fileName);
        }
        return resource.fileName;
    }
    async exportResource(ids) {
        const result = [];
        const resources = (await this.sqlModule
            .table(this.config.resource.tableName)
            .where()
            .in('id', ids)
            .select()).map(r => {
            r.definition = JSON.parse(r.definition || '{}');
            return r;
        });
        result.push(...resources);
        for (const { groupId } of resources) {
            const index = result.findIndex(r => r.id === groupId);
            if (index != -1) {
                result.splice(index, 1);
            }
            const groupResource = await this.findById(groupId);
            if (groupResource != null) {
                result.unshift(groupResource);
            }
        }
        return result;
    }
    async importResource(resources, mode) {
        for (const resource of resources) {
            const data = {
                ...resource,
                definition: JSON.stringify((0, utils_1.isNotNull)(resource.definition) ? resource.definition : {}),
            };
            const namedTable = this.sqlModule
                .table(this.config.resource.tableName)
                .withBlank()
                .primary('id', resource.id);
            if (mode === 'full') {
                await namedTable.insert(data);
            }
            else if (mode === 'increment') {
                const oldResource = await this.findById(resource.id);
                if (oldResource != null) {
                    await namedTable.update(data);
                }
                else {
                    await namedTable.insert(data);
                }
            }
        }
    }
    async deleteAll() {
        this.sqlModule.createQueryRunner();
        await this.sqlModule.queryRunner.clearTable(this.config.resource.tableName);
        // await this.sqlModule.queryRunner.commitTransaction();
    }
}
let MagicResourceService = MagicResourceService_1 = class MagicResourceService {
    async init() {
        const sqlModule = await (0, core_1.getCurrentMainApp)()
            .getApplicationContext()
            .getAsync(modules_1.SQLModule);
        this.importFileStore = new MagicResourceFileStore();
        this.exportFileStore = new MagicResourceFileStore(path.join(os.tmpdir(), 'magic-node'));
        MagicResourceService_1._MAGIC_RESOURCE_STORES.push(new MagicResourceFileStore(path.join(process.cwd(), this.magicConfig.resource.location || '/data/script')), new MagicResourceDatabaseStore({
            sqlModule: sqlModule,
            config: this.magicConfig,
        }));
        await this.setActiveStore();
    }
    /**
     * 设置存储store
     * @private
     */
    async setActiveStore() {
        this.activeStore = MagicResourceService_1._MAGIC_RESOURCE_STORES.filter(s => s.type === this.magicConfig.resource.type)[0];
        if (this.activeStore instanceof MagicResourceFileStore) {
            await this.activeStore.reloadResources();
        }
    }
    /**
     * 注册store
     * @param store
     */
    static registerStore(store) {
        MagicResourceService_1._MAGIC_RESOURCE_STORES.push(store);
    }
    deleteById(id) {
        return this.activeStore.deleteById(id);
    }
    findByGroupId(type, groupId, isFolder = false) {
        return this.activeStore.findByGroupId(type, groupId, isFolder);
    }
    findById(id) {
        return this.activeStore.findById(id);
    }
    findByType(type, enabled = true, isFolder = false) {
        return this.activeStore.findByType(type, enabled, isFolder);
    }
    lock(id, fileLock) {
        return this.activeStore.lock(id, fileLock);
    }
    saveOrUpdate(resource) {
        return this.activeStore.saveOrUpdate(resource);
    }
    getMappingPath(resource) {
        return this.activeStore.getMappingPath(resource);
    }
    getMappingName(resource) {
        return this.activeStore.getMappingName(resource);
    }
    getTreeByType(type, groupId = '0') {
        return this.activeStore.getTreeByType(type, groupId);
    }
    exportResource(ids) {
        return this.activeStore.exportResource(ids);
    }
    importResource(resources, mode) {
        return this.activeStore.importResource(resources, mode);
    }
    deleteAll() {
        return this.activeStore.deleteAll();
    }
    /**
     * 导出ZIP
     * @param ids 资源ID
     */
    async exportResourceZip(ids) {
        const resources = await this.exportResource(ids);
        for (const resource of resources) {
            await this.exportFileStore.saveFile(resource);
        }
        const dest = path.join(this.exportFileStore.basePath, '..', 'magic-node.zip');
        await compressing_1.zip.compressDir(this.exportFileStore.basePath, dest, {
            ignoreBase: true,
        });
        const file = await fse.readFile(dest);
        await fse.rm(this.exportFileStore.basePath, {
            recursive: true,
            force: true,
        });
        await fse.rm(dest);
        return file;
    }
    /**
     * 导入zip
     * @param zipFilePath 文件路径
     * @param mode 导入模式
     */
    async importResourceZip(zipFilePath, mode) {
        if (mode === 'full') {
            await this.activeStore.deleteAll();
        }
        const destPath = path.join(zipFilePath, '..', 'magic-node');
        // console.log(destPath);
        if (await fse.pathExists(destPath)) {
            await fse.rm(destPath, { recursive: true, force: true });
        }
        await compressing_1.zip.uncompress(zipFilePath, destPath);
        this.importFileStore.basePath = destPath;
        await this.importFileStore.reloadResources();
        const resources = (await this.importFileStore.select())
            .map(r => r.resource)
            .sort((a, b) => {
            if (a.method === 'Group') {
                return -1;
            }
            return 0;
        });
        await this.activeStore.importResource(resources, mode);
        await fse.rm(destPath, { recursive: true, force: true });
        //重新初始化
        await magic_provide_1.MagicProvideContainer.reInit();
    }
};
MagicResourceService._MAGIC_RESOURCE_STORES = [];
__decorate([
    (0, decorator_1.Config)('magic'),
    __metadata("design:type", Object)
], MagicResourceService.prototype, "magicConfig", void 0);
MagicResourceService = MagicResourceService_1 = __decorate([
    (0, decorator_1.Provide)(),
    (0, decorator_1.Scope)(decorator_1.ScopeEnum.Singleton)
], MagicResourceService);
exports.MagicResourceService = MagicResourceService;
//# sourceMappingURL=MagicResourceService.js.map