"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LSAndTSDocResolver = void 0;
const path_1 = require("path");
const typescript_1 = __importDefault(require("typescript"));
const utils_1 = require("../../utils");
const service_1 = require("./service");
const SnapshotManager_1 = require("./SnapshotManager");
const utils_2 = require("./utils");
class LSAndTSDocResolver {
    constructor(docManager, workspaceUris, configManager, options) {
        this.docManager = docManager;
        this.workspaceUris = workspaceUris;
        this.configManager = configManager;
        this.options = options;
        /**
         * Create a svelte document -> should only be invoked with svelte files.
         */
        this.createDocument = (fileName, content) => {
            const uri = (0, utils_1.pathToUrl)(fileName);
            const document = this.docManager.openDocument({
                text: content,
                uri
            });
            this.docManager.lockDocument(uri);
            return document;
        };
        this.globalSnapshotsManager = new SnapshotManager_1.GlobalSnapshotsManager(this.lsDocumentContext.tsSystem);
        this.extendedConfigCache = new Map();
        const handleDocumentChange = (document) => {
            // This refreshes the document in the ts language service
            this.getSnapshot(document);
        };
        docManager.on('documentChange', (0, utils_1.debounceSameArg)(handleDocumentChange, (newDoc, prevDoc) => newDoc.uri === prevDoc?.uri, 1000));
        // New files would cause typescript to rebuild its type-checker.
        // Open it immediately to reduce rebuilds in the startup
        // where multiple files and their dependencies
        // being loaded in a short period of times
        docManager.on('documentOpen', (document) => {
            handleDocumentChange(document);
            docManager.lockDocument(document.uri);
        });
        this.getCanonicalFileName = (0, utils_1.createGetCanonicalFileName)((options?.tsSystem ?? typescript_1.default.sys).useCaseSensitiveFileNames);
    }
    get lsDocumentContext() {
        return {
            ambientTypesSource: this.options?.isSvelteCheck ? 'svelte-check' : 'svelte2tsx',
            createDocument: this.createDocument,
            transformOnTemplateError: !this.options?.isSvelteCheck,
            globalSnapshotsManager: this.globalSnapshotsManager,
            notifyExceedSizeLimit: this.options?.notifyExceedSizeLimit,
            extendedConfigCache: this.extendedConfigCache,
            onProjectReloaded: this.options?.onProjectReloaded,
            watchTsConfig: !!this.options?.watchTsConfig,
            tsSystem: this.options?.tsSystem ?? typescript_1.default.sys
        };
    }
    async getLSForPath(path) {
        return (await this.getTSService(path)).getService();
    }
    async getLSAndTSDoc(document) {
        const lang = await this.getLSForPath(document.getFilePath() || '');
        const tsDoc = await this.getSnapshot(document);
        const userPreferences = this.getUserPreferences(tsDoc);
        return { tsDoc, lang, userPreferences };
    }
    async getSnapshot(pathOrDoc) {
        const filePath = typeof pathOrDoc === 'string' ? pathOrDoc : pathOrDoc.getFilePath() || '';
        const tsService = await this.getTSService(filePath);
        return tsService.updateSnapshot(pathOrDoc);
    }
    /**
     * Updates snapshot path in all existing ts services and retrieves snapshot
     */
    async updateSnapshotPath(oldPath, newPath) {
        for (const snapshot of this.globalSnapshotsManager.getByPrefix(oldPath)) {
            await this.deleteSnapshot(snapshot.filePath);
        }
        // This may not be a file but a directory, still try
        await this.getSnapshot(newPath);
    }
    /**
     * Deletes snapshot in all existing ts services
     */
    async deleteSnapshot(filePath) {
        await (0, service_1.forAllServices)((service) => service.deleteSnapshot(filePath));
        const uri = (0, utils_1.pathToUrl)(filePath);
        if (this.docManager.get(uri)) {
            // Guard this call, due to race conditions it may already have been closed;
            // also this may not be a Svelte file
            this.docManager.closeDocument(uri);
        }
        this.docManager.releaseDocument(uri);
    }
    /**
     * Updates project files in all existing ts services
     */
    async updateProjectFiles() {
        await (0, service_1.forAllServices)((service) => service.updateProjectFiles());
    }
    /**
     * Updates file in all ts services where it exists
     */
    async updateExistingTsOrJsFile(path, changes) {
        path = (0, utils_1.normalizePath)(path);
        // Only update once because all snapshots are shared between
        // services. Since we don't have a current version of TS/JS
        // files, the operation wouldn't be idempotent.
        let didUpdate = false;
        await (0, service_1.forAllServices)((service) => {
            if (service.hasFile(path) && !didUpdate) {
                didUpdate = true;
                service.updateTsOrJsFile(path, changes);
            }
        });
    }
    /**
     * @internal Public for tests only
     */
    async getSnapshotManager(filePath) {
        return (await this.getTSService(filePath)).snapshotManager;
    }
    async getTSService(filePath) {
        if (this.options?.tsconfigPath) {
            return (0, service_1.getServiceForTsconfig)(this.options?.tsconfigPath, (0, path_1.dirname)(this.options.tsconfigPath), this.lsDocumentContext);
        }
        if (!filePath) {
            throw new Error('Cannot call getTSService without filePath and without tsconfigPath');
        }
        return (0, service_1.getService)(filePath, this.workspaceUris, this.lsDocumentContext);
    }
    getUserPreferences(tsDoc) {
        const configLang = tsDoc.scriptKind === typescript_1.default.ScriptKind.TS || tsDoc.scriptKind === typescript_1.default.ScriptKind.TSX
            ? 'typescript'
            : 'javascript';
        const nearestWorkspaceUri = this.workspaceUris.find((workspaceUri) => (0, utils_2.isSubPath)(workspaceUri, tsDoc.filePath, this.getCanonicalFileName));
        return this.configManager.getTsUserPreferences(configLang, nearestWorkspaceUri ? (0, utils_1.urlToPath)(nearestWorkspaceUri) : null);
    }
}
exports.LSAndTSDocResolver = LSAndTSDocResolver;
//# sourceMappingURL=LSAndTSDocResolver.js.map