import { __decorate, __metadata } from "tslib";
import { fsExtra, isFilesystemReady } from '@codeblitzjs/ide-sumi-core';
import { InlineChatHandler } from '@opensumi/ide-ai-native/lib/browser/widget/inline-chat/inline-chat.handler';
import { AppConfig, ClientAppContribution, EDITOR_COMMANDS } from '@opensumi/ide-core-browser';
import { Disposable, DisposableStore, Domain, Emitter, Event, ILogger, isArray, Sequencer, URI, } from '@opensumi/ide-core-common';
import { WorkbenchEditorService } from '@opensumi/ide-editor';
import { Selection, SelectionDirection } from '@opensumi/ide-monaco';
import { Autowired } from '@opensumi/di';
import { InlineChatController } from '@opensumi/ide-ai-native/lib/browser/widget/inline-chat/inline-chat-controller';
import { InlineDiffHandler } from '@opensumi/ide-ai-native/lib/browser/widget/inline-diff/inline-diff.handler';
import { EResultKind } from '@opensumi/ide-ai-native/lib/common';
import { MenuContribution, MenuId } from '@opensumi/ide-core-browser/lib/menu/next';
import { IEditorDocumentModelService } from '@opensumi/ide-editor/lib/browser';
import { listenReadable, SumiReadableStream } from '@opensumi/ide-utils/lib/stream';
import path from 'path';
import { IDiffViewerProps } from '../common';
import { removeStart } from '../utils';
let DiffViewerContribution = class DiffViewerContribution {
    constructor() {
        this._disposables = new DisposableStore();
        this._onPartialEditEvent = this._disposables.add(new Emitter());
        this.onPartialEditEvent = this._onPartialEditEvent.event;
        this._onDidTabChange = this._disposables.add(new Emitter());
        this.onDidTabChange = this._onDidTabChange.event;
        this.openFileInTab = async (filePath, content, options) => {
            const fullPath = this.getFullPath(filePath);
            if (!fsExtra.pathExistsSync(fullPath)) {
                fsExtra.ensureFileSync(fullPath);
                fsExtra.writeFileSync(fullPath, content);
            }
            const uri = URI.file(fullPath);
            return {
                uri,
                result: await this.workbenchEditorService.open(uri, options),
            };
        };
        this._openDiffInTab = async (filePath, oldContent, newContent, options) => {
            const { uri, result: openResourceResult } = await this.openFileInTab(filePath, oldContent, Object.assign(Object.assign({}, options), { preview: false }));
            if (!openResourceResult) {
                throw new Error('Failed to open file in tab: ' + filePath);
            }
            const editor = openResourceResult.group.codeEditor;
            const index = openResourceResult.group.resources.indexOf(openResourceResult.resource);
            if (oldContent === newContent) {
                this.inlineDiffHandler.destroyPreviewer();
                return;
            }
            const model = this.editorDocumentModelService.getModelReference(uri);
            if (!model || !model.instance) {
                throw new Error('Failed to get model reference: ' + filePath);
            }
            const monacoModel = model.instance.getMonacoModel();
            monacoModel.setValue(oldContent);
            const fullRange = monacoModel.getFullModelRange();
            const previewer = this.inlineDiffHandler.createDiffPreviewer(editor.monacoEditor, Selection.fromRange(fullRange, SelectionDirection.LTR), {
                disposeWhenEditorClosed: false,
            });
            const whenReady = Event.toPromise(previewer.getNode().onDidEditChange);
            previewer.setValue(newContent);
            await whenReady;
            const diffInfo = this.getDiffInfoForUri(uri);
            if (diffInfo) {
                // 因为 onTabChange 时机早于应用上 diff 的时机，这里补发一个 onDidTabChange 事件
                this._onDidTabChange.fire({
                    currentIndex: index,
                    diffNum: diffInfo.unresolved,
                    newPath: filePath,
                });
            }
            previewer.layout();
            previewer.revealFirstDiff();
        };
        this.sequencer = new Sequencer();
        this.openDiffInTab = async (filePath, oldContent, newContent, options) => {
            await this.sequencer.queue(() => this._openDiffInTab(filePath, oldContent, newContent, options));
        };
        this.openDiffInTabByStream = async (filePath, oldContent, stream, options) => {
            const { uri, result: openResourceResult } = await this.openFileInTab(filePath, oldContent, Object.assign(Object.assign({}, options), { preview: false }));
            if (!openResourceResult) {
                throw new Error('Failed to open file in tab: ' + filePath);
            }
            const editor = openResourceResult.group.codeEditor;
            const model = this.editorDocumentModelService.getModelReference(uri);
            if (!model || !model.instance) {
                throw new Error('Failed to get model reference: ' + filePath);
            }
            const monacoModel = model.instance.getMonacoModel();
            monacoModel.setValue(oldContent);
            const fullRange = monacoModel.getFullModelRange();
            const controller = new InlineChatController();
            const newStream = new SumiReadableStream();
            controller.mountReadable(newStream);
            listenReadable(stream, {
                onData(data) {
                    newStream.emitData({
                        kind: 'content',
                        content: data,
                    });
                },
                onEnd() {
                    newStream.end();
                },
                onError(error) {
                    newStream.emitError(error);
                },
            });
            this.inlineDiffHandler.showPreviewerByStream(editor.monacoEditor, {
                crossSelection: Selection.fromRange(fullRange, SelectionDirection.LTR),
                chatResponse: controller,
                previewerOptions: {
                    disposeWhenEditorClosed: false,
                },
            });
        };
        this.getFilePathForEditor = (editor) => {
            return this.stripDirectory(editor.currentUri.codeUri.fsPath);
        };
        this.getAllTabs = () => {
            const editorGroup = this.workbenchEditorService.editorGroups[0];
            const resources = editorGroup.resources;
            return resources.map((editor, idx) => ({
                index: idx,
                filePath: this.stripDirectory(editor.uri.codeUri.fsPath),
            }));
        };
        this.getFileIndex = (filePath) => {
            const aPath = this.stripDirectory(filePath);
            return this.getAllTabs().findIndex((tab) => tab.filePath === aPath);
        };
        this.getDiffInfoForUri = (uri) => {
            const result = {
                unresolved: 0,
                total: 0,
                toAddedLines: 0,
                toChangedLines: 0,
            };
            let resourceDiff = this.inlineDiffHandler._previewerNodeStore.get(uri.toString());
            if (!resourceDiff) {
                const previewer = this.inlineDiffHandler.getPreviewer();
                if (previewer && previewer.isModel(uri.toString())) {
                    resourceDiff = previewer.getNode();
                }
            }
            if (resourceDiff) {
                const snapshot = resourceDiff.createSnapshot();
                const list = snapshot.decorationSnapshotData.partialEditWidgetList;
                const unresolved = list.filter(v => v.status === 'pending');
                result.total = list.length;
                result.unresolved = unresolved.length;
                const codeInfo = this.getTotalCodeInfo(snapshot);
                result.toAddedLines = codeInfo.unresolvedAddedLinesCount;
                result.toChangedLines = codeInfo.unresolvedChangedLinesCount;
            }
            return result;
        };
    }
    getFullPath(filePath) {
        return path.join(this.appConfig.workspaceDir, filePath);
    }
    getFullPathUri(filePath) {
        return URI.file(this.getFullPath(filePath));
    }
    stripDirectory(filePath) {
        const result = removeStart(filePath, this.appConfig.workspaceDir);
        if (result.startsWith('/')) {
            return result.slice(1);
        }
        return result;
    }
    /**
     * 获取当前编辑器的代码采纳状态
     * 1. 已经采纳的代码信息
     * 2. 还未处理的代码信息
     */
    getTotalCodeInfo(snapshot) {
        const partialEditWidgetList = snapshot.decorationSnapshotData.partialEditWidgetList;
        // 代码除了新增和删除行，还需要统计变更行
        // 1. 新增 N 行 => N
        // 2. 删除 N 行 => N
        // 3. 新增 M 行，删除 N 行 => max(M, N)
        // 综上所述，变更行数 = sum(list.map(item => max(新增行数, 删除行数)))
        const resolvedStatus = caculate(partialEditWidgetList);
        const unresolvedStatus = { added: 0, deleted: 0, changed: 0 };
        partialEditWidgetList.forEach((v, idx) => {
            if (v.status === 'pending') {
                const addedDec = snapshot.decorationSnapshotData.addedDecList[idx];
                const removedWidget = snapshot.decorationSnapshotData.removedWidgetList[idx];
                const addedLinesCount = (addedDec === null || addedDec === void 0 ? void 0 : addedDec.length) || 0;
                const deletedLinesCount = (removedWidget === null || removedWidget === void 0 ? void 0 : removedWidget.height) || 0;
                unresolvedStatus.added += addedLinesCount;
                unresolvedStatus.deleted += deletedLinesCount;
                unresolvedStatus.changed += Math.max(addedLinesCount, deletedLinesCount);
            }
        });
        return {
            totalAddedLinesCount: resolvedStatus.added,
            totalDeletedLinesCount: resolvedStatus.deleted,
            totalChangedLinesCount: resolvedStatus.changed,
            unresolvedAddedLinesCount: unresolvedStatus.added,
            unresolvedDeletedLinesCount: unresolvedStatus.deleted,
            unresolvedChangedLinesCount: unresolvedStatus.changed,
        };
        function caculate(list) {
            const result = { added: 0, deleted: 0, changed: 0 };
            list.forEach((widget) => {
                const addedLinesCount = widget.addedLinesCount;
                const deletedLinesCount = widget.deletedLinesCount;
                result.added += addedLinesCount;
                result.deleted += deletedLinesCount;
                result.changed += Math.max(addedLinesCount, deletedLinesCount);
            });
            return result;
        }
    }
    async initialize() {
        await isFilesystemReady();
        const disposable = new Disposable();
        this._disposables.add(disposable);
        disposable.addDispose(this.inlineDiffHandler.onPartialEditEvent((e) => {
            const fsPath = e.uri.fsPath;
            this._onPartialEditEvent.fire(Object.assign({ filePath: this.stripDirectory(fsPath) }, e));
        }));
        disposable.addDispose(this.workbenchEditorService.onActiveResourceChange((e) => {
            const _newPath = e === null || e === void 0 ? void 0 : e.uri.codeUri.fsPath;
            let newPath = _newPath;
            let currentIndex = -1;
            if (newPath) {
                currentIndex = this.getFileIndex(newPath);
                newPath = this.stripDirectory(newPath);
            }
            const event = {
                newPath,
                currentIndex,
                diffNum: 0,
            };
            if (e === null || e === void 0 ? void 0 : e.uri) {
                const diffInfo = this.getDiffInfoForUri(e.uri);
                event.diffNum = diffInfo.unresolved;
                Object.assign(event, diffInfo);
            }
            this._onDidTabChange.fire(event);
        }));
        this.diffViewerProps.onRef({
            openDiffInTab: async (filePath, oldContent, newContent, options) => {
                return this.openDiffInTab(filePath, oldContent, newContent, options);
            },
            openDiffInTabByStream: this.openDiffInTabByStream,
            openFileInTab: async (filePath, content, options) => {
                const { uri } = await this.openFileInTab(filePath, content, options);
                return uri;
            },
            openTab: async (filePath, options) => {
                const fullPath = this.getFullPath(filePath);
                const uri = URI.file(fullPath);
                await this.workbenchEditorService.open(uri, Object.assign({}, options));
            },
            closeTab: async (filePath) => {
                await this.workbenchEditorService.close(URI.file(this.getFullPath(filePath)), false);
            },
            onPartialEditEvent: (cb) => {
                return this.onPartialEditEvent(cb);
            },
            getFileContent: async (filePath) => {
                const fullPath = this.getFullPath(filePath);
                return await fsExtra.readFile(fullPath, 'utf-8');
            },
            acceptAllPartialEdit: async () => {
                if (this.inlineDiffHandler) {
                    this.inlineDiffHandler.handleAction(EResultKind.ACCEPT);
                }
            },
            rejectAllPartialEdit: async () => {
                if (this.inlineDiffHandler) {
                    this.inlineDiffHandler.handleAction(EResultKind.DISCARD);
                }
            },
            dispose: () => {
                disposable.dispose();
            },
            getCurrentTab: () => {
                const allTabs = this.getAllTabs();
                const currentEditorFilePath = this.getFilePathForEditor(this.workbenchEditorService.currentEditor);
                const currentTabIdx = allTabs.findIndex((tab) => {
                    return tab.filePath === currentEditorFilePath;
                });
                if (currentTabIdx === -1) {
                    return;
                }
                const uri = this.getFullPathUri(currentEditorFilePath);
                const diffInfo = this.getDiffInfoForUri(uri);
                const fileName = path.basename(currentEditorFilePath);
                return Object.assign({ index: currentTabIdx, filePath: currentEditorFilePath, fileName }, diffInfo);
            },
            getTabAtIndex: (index) => {
                const allTabs = this.getAllTabs();
                return allTabs[index];
            },
            getAllTabs: () => {
                return this.getAllTabs();
            },
            closeAllTab: async () => {
                return this.workbenchEditorService.closeAll();
            },
            onDidTabChange: (cb) => {
                return this.onDidTabChange(cb);
            },
        });
    }
    onDidStart() {
        if (this.diffViewerProps.data && isArray(this.diffViewerProps.data)) {
            this.diffViewerProps.data.forEach((item) => {
                this.openDiffInTab(item.path, item.oldCode, item.newCode);
            });
        }
    }
    registerMenus(registry) {
        registry.unregisterMenuItem(MenuId.EditorTitle, EDITOR_COMMANDS.SPLIT_TO_RIGHT.id);
        registry.unregisterMenuItem(MenuId.EditorTitle, EDITOR_COMMANDS.CLOSE_ALL_IN_GROUP.id);
        registry.unregisterMenuId(MenuId.EditorTitle);
        registry.unregisterMenuId(MenuId.EditorTitleRun);
    }
    dispose() {
        this._disposables.dispose();
    }
};
__decorate([
    Autowired(IDiffViewerProps),
    __metadata("design:type", Object)
], DiffViewerContribution.prototype, "diffViewerProps", void 0);
__decorate([
    Autowired(WorkbenchEditorService),
    __metadata("design:type", WorkbenchEditorService)
], DiffViewerContribution.prototype, "workbenchEditorService", void 0);
__decorate([
    Autowired(InlineChatHandler),
    __metadata("design:type", InlineChatHandler)
], DiffViewerContribution.prototype, "inlineChatHandler", void 0);
__decorate([
    Autowired(InlineDiffHandler),
    __metadata("design:type", InlineDiffHandler)
], DiffViewerContribution.prototype, "inlineDiffHandler", void 0);
__decorate([
    Autowired(IEditorDocumentModelService),
    __metadata("design:type", Object)
], DiffViewerContribution.prototype, "editorDocumentModelService", void 0);
__decorate([
    Autowired(AppConfig),
    __metadata("design:type", Object)
], DiffViewerContribution.prototype, "appConfig", void 0);
__decorate([
    Autowired(ILogger),
    __metadata("design:type", Object)
], DiffViewerContribution.prototype, "logger", void 0);
DiffViewerContribution = __decorate([
    Domain(ClientAppContribution, MenuContribution)
], DiffViewerContribution);
export { DiffViewerContribution };
//# sourceMappingURL=base.js.map