/**
 * 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 { observable, action, flowResult, makeObservable, flow } from 'mobx';
import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
import { assertErrorThrown, LogEvent, ActionState, IllegalStateError, UnsupportedOperationError, guaranteeNonNullable, guaranteeType, exactSearch, } from '@finos/legend-shared';
import { generateSetupRoute } from '../../__lib__/LegendStudioNavigation.js';
import { SANDBOX_SDLC_TAG, WorkspaceType, ImportReport, Project, Review, Workspace, Patch, isProjectSandbox, } from '@finos/legend-server-sdlc';
import { DEFAULT_TAB_SIZE, DEFAULT_TYPEAHEAD_SEARCH_LIMIT, DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH, } from '@finos/legend-application';
import { fetchProjectConfigurationStatus, ProjectConfigurationStatus, } from './ProjectConfigurationStatus.js';
import { GraphManagerState } from '@finos/legend-graph';
export class WorkspaceSetupStore {
    applicationStore;
    sdlcServerClient;
    initState = ActionState.create();
    projects = [];
    currentProject;
    currentProjectConfigurationStatus;
    loadProjectsState = ActionState.create();
    createOrImportProjectState = ActionState.create();
    importProjectSuccessReport;
    showCreateProjectModal = false;
    engineInitializeState = ActionState.create();
    enginePromise;
    createSandboxProjectState = ActionState.create();
    sandboxProject = false;
    hasSandboxAccess;
    sandboxModal = false;
    loadSandboxState = ActionState.create();
    patches = [];
    loadPatchesState = ActionState.create();
    workspaces = [];
    currentWorkspace;
    loadWorkspacesState = ActionState.create();
    createWorkspaceState = ActionState.create();
    showCreateWorkspaceModal = false;
    showAdvancedWorkspaceFilterOptions = false;
    graphManagerState;
    constructor(applicationStore, sdlcServerClient) {
        makeObservable(this, {
            projects: observable,
            currentProject: observable,
            currentProjectConfigurationStatus: observable,
            importProjectSuccessReport: observable,
            showCreateProjectModal: observable,
            workspaces: observable,
            currentWorkspace: observable,
            showAdvancedWorkspaceFilterOptions: observable,
            loadSandboxState: observable,
            showCreateWorkspaceModal: observable,
            sandboxProject: observable,
            createSandboxProjectState: observable,
            engineInitializeState: observable,
            enginePromise: observable,
            sandboxModal: observable,
            hasSandboxAccess: observable,
            setShowCreateProjectModal: action,
            setShowCreateWorkspaceModal: action,
            setShowAdvancedWorkspaceFilterOptions: action,
            setImportProjectSuccessReport: action,
            setSandboxModal: action,
            changeWorkspace: action,
            resetProject: action,
            resetWorkspace: action,
            initialize: flow,
            loadProjects: flow,
            loadSandboxProject: flow,
            changeProject: flow,
            createProject: flow,
            importProject: flow,
            createSandboxProject: flow,
            createWorkspace: flow,
            initializeEngine: flow,
        });
        this.applicationStore = applicationStore;
        this.sdlcServerClient = sdlcServerClient;
        this.graphManagerState = new GraphManagerState(applicationStore.pluginManager, applicationStore.logService);
        if (this.supportsCreatingSandboxProject) {
            flowResult(this.initializeEngine()).catch(applicationStore.alertUnhandledError);
        }
    }
    get supportsCreatingSandboxProject() {
        return this.applicationStore.config.options
            .TEMPORARY__enableCreationOfSandboxProjects;
    }
    setShowCreateProjectModal(val) {
        this.showCreateProjectModal = val;
    }
    setShowCreateWorkspaceModal(val) {
        this.showCreateWorkspaceModal = val;
    }
    setShowAdvancedWorkspaceFilterOptions(val) {
        this.showAdvancedWorkspaceFilterOptions = val;
    }
    setImportProjectSuccessReport(importProjectSuccessReport) {
        this.importProjectSuccessReport = importProjectSuccessReport;
    }
    resetProject() {
        this.currentProject = undefined;
        this.patches = [];
        this.workspaces = [];
        this.currentWorkspace = undefined;
        this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(undefined, undefined, undefined, undefined));
        this.currentProjectConfigurationStatus = undefined;
    }
    resetWorkspace() {
        this.currentWorkspace = undefined;
        if (this.currentProject) {
            this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(this.currentProject.projectId, undefined, undefined, undefined));
        }
    }
    setSandboxModal(val) {
        this.sandboxModal = val;
    }
    *createSandboxProject() {
        try {
            if (!this.hasSandboxAccess) {
                this.setSandboxModal(true);
                return;
            }
            // create sandbox project and pilot workspace
            this.applicationStore.alertService.setBlockingAlert({
                message: 'Creating sandbox project...',
                showLoading: true,
            });
            const sandboxProject = (yield this.graphManagerState.graphManager.createSandboxProject());
            this.applicationStore.alertService.setBlockingAlert({
                message: `Sandbox project ${sandboxProject.projectId} created. Creating default workspace...`,
                showLoading: true,
            });
            yield flowResult(this.loadSandboxProject());
            const sandbox = guaranteeType(this.sandboxProject, Project, 'Error retrieving sandbox project');
            const initialWorkspace = Workspace.serialization.fromJson((yield this.sdlcServerClient.createWorkspace(sandbox.projectId, undefined, 'myWorkspace', WorkspaceType.GROUP)));
            yield flowResult(this.changeProject(sandbox, {
                workspaceId: initialWorkspace.workspaceId,
                workspaceType: WorkspaceType.GROUP,
            }));
            this.applicationStore.alertService.setBlockingAlert(undefined);
            this.applicationStore.notificationService.notifySuccess(`Sandbox project with workspace created`);
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.ENGINE_MANAGER_FAILURE), error);
            this.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.applicationStore.alertService.setBlockingAlert(undefined);
        }
    }
    *initialize(projectId, workspaceId, groupWorkspaceId) {
        if (!this.initState.isInInitialState) {
            return;
        }
        this.initState.inProgress();
        // TODO: when we genericize the way to initialize an application page
        this.applicationStore.assistantService.setIsHidden(false);
        try {
            if (projectId) {
                let project;
                try {
                    project = Project.serialization.fromJson((yield this.sdlcServerClient.getProject(projectId)));
                }
                catch {
                    this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(undefined, undefined));
                    this.initState.pass();
                    return;
                }
                yield flowResult(this.changeProject(project, workspaceId
                    ? { workspaceId: workspaceId, workspaceType: WorkspaceType.USER }
                    : groupWorkspaceId
                        ? {
                            workspaceId: groupWorkspaceId,
                            workspaceType: WorkspaceType.GROUP,
                        }
                        : undefined));
            }
            this.initState.pass();
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
            this.applicationStore.notificationService.notifyError(error);
            this.initState.fail();
        }
    }
    *initializeEngine() {
        this.engineInitializeState.inProgress();
        try {
            const initPromise = this.graphManagerState.graphManager.initialize({
                env: this.applicationStore.config.env,
                tabSize: DEFAULT_TAB_SIZE,
                clientConfig: {
                    baseUrl: this.applicationStore.config.engineServerUrl,
                },
            }, {
                tracerService: this.applicationStore.tracerService,
                disableGraphConfiguration: true,
            });
            this.enginePromise = initPromise;
            yield initPromise;
            this.enginePromise = undefined;
            this.engineInitializeState.complete();
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.ENGINE_MANAGER_FAILURE), error);
            this.engineInitializeState.complete();
        }
    }
    *loadProjects(searchText) {
        const isValidSearchString = searchText.length >= DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH;
        this.loadProjectsState.inProgress();
        try {
            this.projects = (yield this.sdlcServerClient.getProjects(undefined, 
            // We apply an exact search on the input text because we show exact searches with
            // custom selector. This avoids losing some results with the additional filtering.
            isValidSearchString ? exactSearch(searchText) : undefined, undefined, DEFAULT_TYPEAHEAD_SEARCH_LIMIT)).map((v) => Project.serialization.fromJson(v));
            this.loadProjectsState.pass();
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.notificationService.notifyError(error);
            this.loadProjectsState.fail();
        }
    }
    *loadSandboxProject() {
        if (!this.supportsCreatingSandboxProject) {
            return;
        }
        this.sandboxProject = false;
        try {
            this.loadSandboxState.inProgress();
            if (this.enginePromise) {
                yield this.enginePromise;
            }
            const sandboxProject = (yield this.sdlcServerClient.getProjects(undefined, this.sdlcServerClient.currentUser?.userId, [SANDBOX_SDLC_TAG], 1)).map((v) => Project.serialization.fromJson(v));
            if (this.hasSandboxAccess === undefined) {
                this.hasSandboxAccess =
                    (yield this.graphManagerState.graphManager.userHasPrototypeProjectAccess(this.sdlcServerClient.currentUser?.userId ?? ''));
            }
            this.sandboxProject = true;
            if (sandboxProject.length > 1) {
                throw new UnsupportedOperationError('Only one sandbox project is supported per user.');
            }
            else if (sandboxProject.length === 1) {
                this.sandboxProject = guaranteeNonNullable(sandboxProject[0]);
            }
            this.loadSandboxState.pass();
        }
        catch (error) {
            this.sandboxProject = true;
            assertErrorThrown(error);
            this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE), error);
            this.loadSandboxState.fail();
        }
        finally {
            this.loadSandboxState.complete();
        }
    }
    *changeProject(project, workspaceInfo) {
        this.currentProject = project;
        this.currentProjectConfigurationStatus = undefined;
        this.loadPatchesState.inProgress();
        try {
            if (isProjectSandbox(project)) {
                this.patches = [];
            }
            else {
                this.patches = (yield this.sdlcServerClient.getPatches(project.projectId)).map((v) => Patch.serialization.fromJson(v));
            }
            this.loadPatchesState.pass();
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DEPOT_MANAGER_FAILURE), error);
            this.applicationStore.notificationService.notifyError(error);
            this.loadPatchesState.fail();
        }
        this.loadWorkspacesState.inProgress();
        try {
            if (isProjectSandbox(project)) {
                const result = new ProjectConfigurationStatus();
                result.projectId = project.projectId;
                result.isConfigured = true;
                this.currentProjectConfigurationStatus = result;
            }
            else {
                this.currentProjectConfigurationStatus =
                    (yield fetchProjectConfigurationStatus(project.projectId, undefined, this.applicationStore, this.sdlcServerClient));
            }
            const workspacesInConflictResolutionIds = isProjectSandbox(project)
                ? []
                : (yield this.sdlcServerClient.getWorkspacesInConflictResolutionMode(project.projectId, undefined)).map((workspace) => workspace.workspaceId);
            this.workspaces = (yield this.sdlcServerClient.getWorkspaces(project.projectId))
                .map((v) => Workspace.serialization.fromJson(v))
                .filter(
            // NOTE we don't handle workspaces that only have conflict resolution but no standard workspace
            // since that indicates bad state of the SDLC server
            (workspace) => !workspacesInConflictResolutionIds.includes(workspace.workspaceId));
            for (const patch of this.patches) {
                this.workspaces = this.workspaces.concat((yield this.sdlcServerClient.getWorkspaces(project.projectId, patch.patchReleaseVersionId.id))
                    .map((v) => {
                    const w = Workspace.serialization.fromJson(v);
                    w.source = patch.patchReleaseVersionId.id;
                    return w;
                })
                    .filter(
                // NOTE we don't handle workspaces that only have conflict resolution but no standard workspace
                // since that indicates bad state of the SDLC server
                (workspace) => !workspacesInConflictResolutionIds.includes(workspace.workspaceId)));
            }
            if (workspaceInfo) {
                const matchingWorkspace = this.workspaces.find((workspace) => workspace.workspaceType === workspaceInfo.workspaceType &&
                    workspace.workspaceId === workspaceInfo.workspaceId);
                if (matchingWorkspace) {
                    this.changeWorkspace(matchingWorkspace);
                }
                else {
                    this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(project.projectId, undefined));
                }
            }
            else {
                this.currentWorkspace = undefined;
                this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(project.projectId, undefined));
            }
            this.loadWorkspacesState.pass();
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DEPOT_MANAGER_FAILURE), error);
            this.applicationStore.notificationService.notifyError(error);
            this.loadWorkspacesState.fail();
        }
    }
    changeWorkspace(workspace) {
        if (!this.currentProject) {
            throw new IllegalStateError(`Can't change workspace: project is not specified`);
        }
        this.currentWorkspace = workspace;
        this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(this.currentProject.projectId, workspace.source, workspace.workspaceId, workspace.workspaceType));
    }
    *createProject(name, description, groupId, artifactId, tags = []) {
        this.createOrImportProjectState.inProgress();
        try {
            const createdProject = Project.serialization.fromJson((yield this.sdlcServerClient.createProject({
                name,
                description,
                groupId,
                artifactId,
                tags,
            })));
            this.applicationStore.notificationService.notifySuccess(`Project '${name}' is succesfully created`);
            yield flowResult(this.changeProject(createdProject));
            this.setShowCreateProjectModal(false);
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.createOrImportProjectState.reset();
        }
    }
    *importProject(id, groupId, artifactId) {
        this.createOrImportProjectState.inProgress();
        try {
            const report = ImportReport.serialization.fromJson((yield this.sdlcServerClient.importProject({
                id,
                groupId,
                artifactId,
            })));
            const importReview = Review.serialization.fromJson((yield this.sdlcServerClient.getReview(report.project.projectId, undefined, report.reviewId)));
            this.setImportProjectSuccessReport({
                projectName: report.project.name,
                projectId: report.project.projectId,
                reviewUrl: importReview.webURL,
            });
            yield flowResult(this.changeProject(report.project));
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.createOrImportProjectState.reset();
        }
    }
    *createWorkspace(projectId, patchReleaseVersionId, workspaceId, workspaceType) {
        this.createWorkspaceState.inProgress();
        try {
            const newWorkspace = Workspace.serialization.fromJson((yield this.sdlcServerClient.createWorkspace(projectId, patchReleaseVersionId, workspaceId, workspaceType)));
            newWorkspace.source = patchReleaseVersionId;
            this.applicationStore.notificationService.notifySuccess(`Workspace '${newWorkspace.workspaceId}' is succesfully created`);
            const matchingWorkspace = this.workspaces.find((workspace) => workspace.workspaceId === newWorkspace.workspaceId &&
                workspace.workspaceType === newWorkspace.workspaceType);
            const newWorkspaceToSelect = matchingWorkspace ?? newWorkspace;
            this.changeWorkspace(newWorkspaceToSelect);
            this.setShowCreateWorkspaceModal(false);
            // NOTE: do this after closing the modal to not interfere
            // with validation of existing workspaces in create workspace modal
            if (!matchingWorkspace) {
                this.workspaces.push(newWorkspace);
            }
        }
        catch (error) {
            assertErrorThrown(error);
            this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE), error);
            this.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.createWorkspaceState.reset();
        }
    }
}
//# sourceMappingURL=WorkspaceSetupStore.js.map