/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { action, makeObservable, flowResult, observable, flow } from 'mobx';
import { LogEvent, assertErrorThrown, guaranteeNonNullable, NetworkClientError, HttpStatus, } from '@finos/legend-shared';
import { EntityDiffViewState } from '../editor-state/entity-diff-editor-state/EntityDiffViewState.js';
import { SPECIAL_REVISION_ALIAS } from '../editor-state/entity-diff-editor-state/EntityDiffEditorState.js';
import { EntityChangeConflictEditorState } from '../editor-state/entity-diff-editor-state/EntityChangeConflictEditorState.js';
import { WorkspaceUpdateReportStatus, EntityDiff, Review, ReviewState, Revision, RevisionAlias, } from '@finos/legend-server-sdlc';
import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
export class WorkspaceUpdaterState {
    editorStore;
    sdlcState;
    committedReviewsBetweenWorkspaceBaseAndProjectLatest = [];
    isUpdatingWorkspace = false;
    isRefreshingWorkspaceUpdater = false;
    constructor(editorStore, sdlcState) {
        makeObservable(this, {
            committedReviewsBetweenWorkspaceBaseAndProjectLatest: observable,
            isUpdatingWorkspace: observable,
            isRefreshingWorkspaceUpdater: observable,
            openProjectLatestChange: action,
            openPotentialWorkspaceUpdateConflict: action,
            refreshWorkspaceUpdater: flow,
            updateWorkspace: flow,
            fetchLatestCommittedReviews: flow,
        });
        this.editorStore = editorStore;
        this.sdlcState = sdlcState;
    }
    openProjectLatestChange(diff) {
        const fromEntityGetter = (entityPath) => entityPath
            ? this.editorStore.changeDetectionState.workspaceBaseRevisionState.entities.find((e) => e.path === entityPath)
            : undefined;
        const toEntityGetter = (entityPath) => entityPath
            ? this.editorStore.changeDetectionState.projectLatestRevisionState.entities.find((e) => e.path === entityPath)
            : undefined;
        const fromEntity = EntityDiff.shouldOldEntityExist(diff)
            ? guaranteeNonNullable(fromEntityGetter(diff.getValidatedOldPath()), `Can't find entity with path  '${diff.oldPath}'`)
            : undefined;
        const toEntity = EntityDiff.shouldNewEntityExist(diff)
            ? guaranteeNonNullable(toEntityGetter(diff.getValidatedNewPath()), `Can't find entity with path  '${diff.newPath}'`)
            : undefined;
        this.editorStore.tabManagerState.openTab(new EntityDiffViewState(this.editorStore, SPECIAL_REVISION_ALIAS.WORKSPACE_BASE, SPECIAL_REVISION_ALIAS.PROJECT_HEAD, diff.oldPath, diff.newPath, fromEntity, toEntity, fromEntityGetter, toEntityGetter));
    }
    openPotentialWorkspaceUpdateConflict(conflict) {
        const baseEntityGetter = (entityPath) => entityPath
            ? this.editorStore.changeDetectionState.workspaceBaseRevisionState.entities.find((e) => e.path === entityPath)
            : undefined;
        const currentChangeEntityGetter = (entityPath) => entityPath
            ? this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.entities.find((e) => e.path === entityPath)
            : undefined;
        const incomingChangeEntityGetter = (entityPath) => entityPath
            ? this.editorStore.changeDetectionState.projectLatestRevisionState.entities.find((e) => e.path === entityPath)
            : undefined;
        const conflictEditorState = new EntityChangeConflictEditorState(this.editorStore, this.editorStore.conflictResolutionState, conflict.entityPath, SPECIAL_REVISION_ALIAS.WORKSPACE_BASE, SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD, SPECIAL_REVISION_ALIAS.PROJECT_HEAD, baseEntityGetter(conflict.entityPath), currentChangeEntityGetter(conflict.entityPath), incomingChangeEntityGetter(conflict.entityPath), baseEntityGetter, currentChangeEntityGetter, incomingChangeEntityGetter);
        conflictEditorState.setReadOnly(true);
        this.editorStore.tabManagerState.openTab(conflictEditorState);
    }
    *refreshWorkspaceUpdater() {
        // check if the workspace is in conflict resolution mode
        try {
            const isInConflictResolutionMode = (yield flowResult(this.sdlcState.checkIfCurrentWorkspaceIsInConflictResolutionMode()));
            if (isInConflictResolutionMode) {
                this.editorStore.applicationStore.alertService.setBlockingAlert({
                    message: 'Workspace is in conflict resolution mode',
                    prompt: 'Please refresh the application',
                });
                return;
            }
        }
        catch (error) {
            assertErrorThrown(error);
            if (error instanceof NetworkClientError &&
                error.response.status === HttpStatus.NOT_FOUND) {
                this.editorStore.applicationStore.alertService.setBlockingAlert({
                    message: 'Current project or workspace no longer exists',
                    prompt: 'Please refresh the application',
                });
            }
            else {
                this.editorStore.applicationStore.notificationService.notifyWarning('Failed to check if current workspace is in conflict resolution mode');
            }
            return;
        }
        try {
            this.isRefreshingWorkspaceUpdater = true;
            this.sdlcState.isWorkspaceOutdated =
                (yield this.editorStore.sdlcServerClient.isWorkspaceOutdated(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace));
            if (!this.sdlcState.isWorkspaceOutdated) {
                this.editorStore.changeDetectionState.setAggregatedProjectLatestChanges([]);
                this.committedReviewsBetweenWorkspaceBaseAndProjectLatest = [];
                return; // no need to do anything else if workspace is up to date
            }
            yield flowResult(this.fetchLatestCommittedReviews());
            // ======= (RE)START CHANGE DETECTION =======
            const restartChangeDetectionStartTime = Date.now();
            this.editorStore.changeDetectionState.stop();
            yield Promise.all([
                this.sdlcState.buildProjectLatestRevisionEntityHashesIndex(),
                this.sdlcState.buildWorkspaceBaseRevisionEntityHashesIndex(),
            ]);
            this.editorStore.changeDetectionState.start();
            yield Promise.all([
                this.editorStore.changeDetectionState.computeAggregatedProjectLatestChanges(true),
                this.editorStore.changeDetectionState.computeAggregatedWorkspaceChanges(true),
            ]);
            this.editorStore.applicationStore.logService.info(LogEvent.create(LEGEND_STUDIO_APP_EVENT.CHANGE_DETECTION_RESTART__SUCCESS), Date.now() - restartChangeDetectionStartTime, 'ms');
            // ======= FINISHED (RE)START CHANGE DETECTION =======
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
            this.sdlcState.handleChangeDetectionRefreshIssue(error);
        }
        finally {
            this.isRefreshingWorkspaceUpdater = false;
        }
    }
    *updateWorkspace() {
        if (this.isUpdatingWorkspace) {
            return;
        }
        const startTime = Date.now();
        // TODO: we might need to check if the workspace is up-to-date before allowing update
        // check if the workspace is in conflict resolution mode
        try {
            const isInConflictResolutionMode = (yield flowResult(this.sdlcState.checkIfCurrentWorkspaceIsInConflictResolutionMode()));
            if (isInConflictResolutionMode) {
                this.editorStore.applicationStore.alertService.setBlockingAlert({
                    message: 'Workspace is in conflict resolution mode',
                    prompt: 'Please refresh the application',
                });
                return;
            }
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.notificationService.notifyWarning('Failed to check if current workspace is in conflict resolution mode');
            return;
        }
        this.isUpdatingWorkspace = true;
        try {
            this.editorStore.applicationStore.alertService.setBlockingAlert({
                message: 'Updating workspace...',
                prompt: 'Please do not close the application',
                showLoading: true,
            });
            const workspaceUpdateReport = (yield this.editorStore.sdlcServerClient.updateWorkspace(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace));
            this.editorStore.applicationStore.logService.info(LogEvent.create(LEGEND_STUDIO_APP_EVENT.UPDATE_WORKSPACE__SUCCESS), Date.now() - startTime, 'ms');
            this.sdlcState.isWorkspaceOutdated = false;
            switch (workspaceUpdateReport.status) {
                // TODO: we might want to handle the situation more gracefully rather than just reloading the page
                case WorkspaceUpdateReportStatus.CONFLICT:
                case WorkspaceUpdateReportStatus.UPDATED:
                    this.editorStore.applicationStore.navigationService.navigator.reload({
                        ignoreBlocking: true,
                    });
                    break;
                case WorkspaceUpdateReportStatus.NO_OP:
                default:
                    break;
            }
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.editorStore.applicationStore.alertService.setBlockingAlert(undefined);
            this.isUpdatingWorkspace = false;
        }
    }
    /**
     * Fetch committed reviews between workspace base and project latest
     */
    *fetchLatestCommittedReviews() {
        try {
            // we find the review associated with the workspace base, this usually exist, except in 2 cases:
            // 1. the revision is somehow directly added to the branch by the user (in the case of `git`, user directly pushed to unprotected default branch)
            // 2. the revision is the merged/comitted review revision (this usually happens for projects where fast forwarding merging is not default)
            // in those case, we will get the time from the base revision
            const workspaceBaseRevision = Revision.serialization.fromJson((yield this.editorStore.sdlcServerClient.getRevision(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, RevisionAlias.BASE)));
            const baseReviewObj = (yield this.editorStore.sdlcServerClient.getReviews(this.sdlcState.activeProject.projectId, this.sdlcState.activePatch?.patchReleaseVersionId.id, {
                state: ReviewState.COMMITTED,
                revisionIds: [workspaceBaseRevision.id],
                limit: 1,
            }))[0];
            const baseReview = baseReviewObj
                ? Review.serialization.fromJson(baseReviewObj)
                : undefined;
            this.committedReviewsBetweenWorkspaceBaseAndProjectLatest = (yield this.editorStore.sdlcServerClient.getReviews(this.sdlcState.activeProject.projectId, this.sdlcState.activePatch?.patchReleaseVersionId.id, {
                state: ReviewState.COMMITTED,
                since: baseReview
                    ? baseReview.committedAt
                    : workspaceBaseRevision.committedAt,
            }))
                .map((v) => Review.serialization.fromJson(v))
                .filter((review) => !baseReview || review.id !== baseReview.id); // make sure to exclude the base review
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
    }
}
//# sourceMappingURL=WorkspaceUpdaterState.js.map