"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
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.LocalAdapter = void 0;
const fs = require("fs");
const path = require("path");
const util = require("util");
const StorageUtils_1 = require("../Utilities/StorageUtils");
const internal_1 = require("../internal");
let readFile, writeFile, stat, mkdir, readdir;
if (fs.readFile) {
    readFile = util.promisify(fs.readFile);
    writeFile = util.promisify(fs.writeFile);
    stat = util.promisify(fs.stat);
    mkdir = util.promisify(fs.mkdir);
    readdir = util.promisify(fs.readdir);
}
class LocalAdapter extends internal_1.StorageAdapterBase {
    constructor(root) {
        super();
        /**
         * @internal
         */
        this.type = 'local';
        if (root) {
            this.root = root;
            this.fullRoot = path.resolve(root);
        }
    }
    canRead() {
        return true;
    }
    readAsync(corpusPath) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const adapterPath = this.createAdapterPath(corpusPath);
                const s = yield readFile(adapterPath, 'utf-8');
                if (s === undefined || s === '') {
                    throw new Error(`The requested document '${adapterPath}' is empty`);
                }
                return s;
            }
            catch (err) {
                throw (err);
            }
        });
    }
    canWrite() {
        return true;
    }
    writeAsync(corpusPath, data) {
        return __awaiter(this, void 0, void 0, function* () {
            // ensure that the path exists before trying to write the file
            const adapterPath = this.createAdapterPath(corpusPath);
            if ((yield this.ensurePath(adapterPath)) === false) {
                throw new Error(`could not create folder for document '${adapterPath}'`);
            }
            yield writeFile(adapterPath, data, 'utf-8');
        });
    }
    createAdapterPath(corpusPath) {
        const pathTuple = StorageUtils_1.StorageUtils.splitNamespacePath(corpusPath);
        if (!pathTuple) {
            return undefined;
        }
        corpusPath = pathTuple[1];
        if (path.isAbsolute(this.fullRoot)) {
            return path.join(this.fullRoot, corpusPath);
        }
        return path.join(__dirname, this.root, corpusPath);
    }
    createCorpusPath(adapterPath) {
        if (!adapterPath || adapterPath.startsWith('http')) {
            return;
        }
        // make this a file system path and normalize it
        const formattedAdapterPath = path.resolve(adapterPath)
            .replace(/\\/g, '/');
        const formattedRoot = this.fullRoot.replace(/\\/g, '/');
        // might not be an adapterPath that we understand. check that first
        if (formattedAdapterPath.startsWith(formattedRoot)) {
            return formattedAdapterPath.slice(formattedRoot.length);
        }
        return; // signal that we didn't recognize path as one for this adapter
    }
    computeLastModifiedTimeAsync(corpusPath) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const adapterPath = this.createAdapterPath(corpusPath);
                const stats = yield stat(adapterPath);
                return stats.mtime;
            }
            catch (err) {
                return undefined;
            }
        });
    }
    fetchAllFilesAsync(folderCorpusPath) {
        return __awaiter(this, void 0, void 0, function* () {
            // Returns a list corpus paths to all files and folders at or under the
            // provided corpus path to a folder
            return this._fetchAllFilesAsync(folderCorpusPath);
        });
    }
    fetchAllFilesMetadataAsync(folderCorpusPath) {
        return __awaiter(this, void 0, void 0, function* () {
            const fileMetadatas = new Map();
            const fileNames = yield this.fetchAllFilesAsync(folderCorpusPath);
            for (const fileName of fileNames) {
                const path = this.createAdapterPath(fileName);
                const stats = yield stat(path);
                fileMetadatas.set(fileName, { fileSizeBytes: stats.isFile ? stats.size : undefined });
            }
            return fileMetadatas;
        });
    }
    fetchConfig() {
        const resultConfig = {
            type: this.type
        };
        const configObject = {
            root: this.root
        };
        if (this.locationHint) {
            configObject.locationHint = this.locationHint;
        }
        resultConfig.config = configObject;
        return JSON.stringify(resultConfig);
    }
    updateConfig(config) {
        if (!config) {
            throw new Error('Local adapter needs a config.');
        }
        const configJson = JSON.parse(config);
        if (!configJson.root) {
            throw new Error('The root has to be specified and cannot be null.');
        }
        this.root = configJson.root;
        if (configJson.locationHint) {
            this.locationHint = configJson.locationHint;
        }
        this.fullRoot = path.resolve(this.root);
    }
    dirExists(folderPath) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const adapterPath = this.createAdapterPath(folderPath);
                const dir = yield stat(adapterPath);
                return dir && dir.isDirectory();
            }
            catch (err) {
                return false;
            }
        });
    }
    // recursive check for / create path to house a document
    ensurePath(pathFor) {
        return __awaiter(this, void 0, void 0, function* () {
            const pathEnd = pathFor.lastIndexOf(path.sep);
            if (pathEnd === -1) {
                return false;
            }
            const pathTo = pathFor.slice(0, pathEnd);
            const dir = yield stat(pathTo);
            if (dir && dir.isDirectory) {
                return true;
            }
            // make sure there is a place to put the directory that is missing
            if ((yield this.ensurePath(pathTo)) === false) {
                return false;
            }
            // hmm how to tell if it fails?
            yield mkdir(pathTo);
            return true;
        });
    }
    _fetchAllFilesAsync(currFullPath) {
        return __awaiter(this, void 0, void 0, function* () {
            let allFiles = [];
            const adapterPath = this.createAdapterPath(currFullPath);
            const content = yield readdir(adapterPath);
            for (const childPath of content) {
                const childCorpusPath = this.createCorpusPath(path.join(adapterPath, childPath));
                const isDir = yield this.dirExists(childCorpusPath);
                if (isDir) {
                    const subFiles = yield this._fetchAllFilesAsync(childCorpusPath);
                    allFiles = allFiles.concat(subFiles);
                }
                else {
                    allFiles.push(childCorpusPath);
                }
            }
            return allFiles;
        });
    }
}
exports.LocalAdapter = LocalAdapter;
//# sourceMappingURL=LocalAdapter.js.map