/**
 * 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 { uuid, assertErrorThrown, LogEvent, ActionState, filterByType, } from '@finos/legend-shared';
import { WorkflowJob, Workflow } from '@finos/legend-server-sdlc';
export class WorkflowExplorerTreeNodeData {
    isSelected;
    isOpen;
    id;
    label;
    childrenIds;
    constructor(id, label) {
        this.id = id;
        this.label = label;
    }
}
const getWorkflowNodeId = (workflowId) => `workflow_${workflowId}`;
const getJobId = (jobId) => `job_${jobId}`;
export class WorkflowTreeNodeData extends WorkflowExplorerTreeNodeData {
    workflow;
    constructor(workflow) {
        super(getWorkflowNodeId(workflow.id), workflow.id);
        this.workflow = workflow;
    }
}
export class WorkflowJobTreeNodeData extends WorkflowExplorerTreeNodeData {
    workflowJob;
    constructor(workflowJob) {
        super(getJobId(workflowJob.id), workflowJob.name);
        this.workflowJob = workflowJob;
    }
}
const addWorkflowJobNodeToTree = (workflowJob, workflowNode, treeData) => {
    const node = new WorkflowJobTreeNodeData(workflowJob);
    if (workflowNode.childrenIds) {
        workflowNode.childrenIds.push(node.id);
    }
    else {
        workflowNode.childrenIds = [node.id];
    }
    treeData.nodes.set(node.id, node);
    return node;
};
const updateWorkflowJobData = (workflowJobs, workflowId, treeData) => {
    const workflowNode = treeData.nodes.get(getWorkflowNodeId(workflowId));
    if (workflowNode instanceof WorkflowTreeNodeData) {
        workflowNode.childrenIds?.forEach((id) => treeData.nodes.delete(id));
        workflowNode.childrenIds = [];
        workflowJobs.forEach((job) => addWorkflowJobNodeToTree(job, workflowNode, treeData));
    }
};
export class WorkflowLogState {
    editorStore;
    workflowManagerState;
    fetchJobLogState = ActionState.create();
    job;
    logs;
    constructor(editorStore, workflowManagerState, job, logs) {
        makeObservable(this, {
            job: observable,
            logs: observable,
            setLogs: action,
            setJob: action,
            closeModal: action,
            refreshJobLogs: flow,
            viewJobLogs: flow,
        });
        this.editorStore = editorStore;
        this.workflowManagerState = workflowManagerState;
        this.job = job;
        this.logs = logs ?? '';
    }
    setLogs(val) {
        this.logs = val;
    }
    setJob(val) {
        this.job = val;
    }
    closeModal() {
        this.setJob(undefined);
        this.setLogs('');
    }
    *refreshJobLogs(workflowJob) {
        try {
            this.fetchJobLogState.inProgress();
            const job = (yield flowResult(this.workflowManagerState.getJob(workflowJob)));
            this.setJob(job);
            const logs = (yield flowResult(this.workflowManagerState.getJobLogs(workflowJob)));
            this.setLogs(logs);
            this.fetchJobLogState.pass();
        }
        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.fetchJobLogState.fail();
        }
    }
    *viewJobLogs(workflowJob) {
        try {
            this.setJob(workflowJob);
            this.fetchJobLogState.inProgress();
            const logs = (yield flowResult(this.workflowManagerState.getJobLogs(workflowJob)));
            this.setLogs(logs);
            this.fetchJobLogState.pass();
        }
        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.fetchJobLogState.fail();
        }
    }
}
export class WorkflowState {
    uuid = uuid();
    editorStore;
    workflowManagerState;
    treeData;
    isExecutingWorkflowRequest = false;
    constructor(editorStore, workflowManagerState, workflow, jobs) {
        makeObservable(this, {
            treeData: observable.ref,
            isExecutingWorkflowRequest: observable,
            setWorkflowTreeData: action,
            fetchAllWorkspaceWorkJobs: flow,
            onTreeNodeSelect: flow,
            cancelJob: flow,
            refreshWorkflow: flow,
            retryJob: flow,
            runManualJob: flow,
        });
        this.editorStore = editorStore;
        this.workflowManagerState = workflowManagerState;
        this.treeData = this.buildTreeData(workflow, jobs);
    }
    setWorkflowTreeData(val) {
        this.treeData = val;
    }
    buildTreeData(workflow, jobs) {
        const rootIds = [];
        const nodes = new Map();
        const treeData = { rootIds, nodes };
        const workflowNode = new WorkflowTreeNodeData(workflow);
        treeData.rootIds.push(workflowNode.id);
        treeData.nodes.set(workflowNode.id, workflowNode);
        if (jobs) {
            workflowNode.isOpen = true;
            updateWorkflowJobData(jobs, workflow.id, treeData);
        }
        return treeData;
    }
    *fetchAllWorkspaceWorkJobs(workflowId, treeData) {
        try {
            this.isExecutingWorkflowRequest = true;
            const workflowJobs = (yield flowResult(this.workflowManagerState.getJobs(workflowId))).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
            updateWorkflowJobData(workflowJobs, workflowId, treeData);
            this.setWorkflowTreeData({ ...treeData });
        }
        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.isExecutingWorkflowRequest = false;
        }
    }
    *onTreeNodeSelect(node, treeData) {
        if (node instanceof WorkflowTreeNodeData) {
            if (!node.childrenIds) {
                yield flowResult(this.fetchAllWorkspaceWorkJobs(node.workflow.id, treeData));
            }
            node.isOpen = !node.isOpen;
        }
        this.setWorkflowTreeData({ ...treeData });
    }
    *cancelJob(workflowJob, treeData) {
        try {
            this.isExecutingWorkflowRequest = true;
            this.workflowManagerState.cancelJob(workflowJob);
            yield flowResult(this.refreshWorkflow(workflowJob.workflowId, treeData));
        }
        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.isExecutingWorkflowRequest = false;
        }
    }
    *refreshWorkflow(workflowId, treeData) {
        const node = treeData.nodes.get(getWorkflowNodeId(workflowId));
        if (node instanceof WorkflowTreeNodeData) {
            const workflow = (yield flowResult(this.workflowManagerState.getWorkflow(workflowId)));
            node.workflow = workflow;
        }
        yield flowResult(this.fetchAllWorkspaceWorkJobs(workflowId, treeData));
    }
    *retryJob(workflowJob, treeData) {
        try {
            this.isExecutingWorkflowRequest = true;
            yield flowResult(this.workflowManagerState.retryJob(workflowJob));
            yield flowResult(this.refreshWorkflow(workflowJob.workflowId, treeData));
        }
        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.isExecutingWorkflowRequest = false;
        }
    }
    *runManualJob(workflowJob, treeData) {
        try {
            this.isExecutingWorkflowRequest = true;
            yield flowResult(this.workflowManagerState.runManualJob(workflowJob));
            yield flowResult(this.refreshWorkflow(workflowJob.workflowId, treeData));
        }
        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.isExecutingWorkflowRequest = false;
        }
    }
}
export class WorkflowManagerState {
    editorStore;
    sdlcState;
    fetchWorkflowsState = ActionState.create();
    logState;
    workflowStates = [];
    constructor(editorStore, sdlcState) {
        makeObservable(this, {
            logState: observable,
            workflowStates: observable,
            fetchAllWorkflows: flow,
            getWorkflows: flow,
            getWorkflow: flow,
            getJobs: flow,
            getJob: flow,
            retryJob: flow,
            cancelJob: flow,
            runManualJob: flow,
            getJobLogs: flow,
        });
        this.editorStore = editorStore;
        this.sdlcState = sdlcState;
        this.logState = new WorkflowLogState(this.editorStore, this, undefined, undefined);
    }
    *fetchAllWorkflows() {
        try {
            this.fetchWorkflowsState.inProgress();
            // NOTE: this network call can take a while, so we might consider limiting the number of workflows to 10 or so
            const workflows = (yield flowResult(this.getWorkflows()));
            const openWorkflowIds = this.workflowStates
                .map((workflowState) => Array.from(workflowState.treeData.nodes.values()))
                .flat()
                .filter(filterByType(WorkflowTreeNodeData))
                .filter((node) => node.isOpen)
                .map((node) => node.workflow.id);
            const jobsIndex = new Map();
            yield Promise.all(workflows
                .filter((workflow) => openWorkflowIds.includes(workflow.id))
                // NOTE: this network call can take a while, so we might consider limiting the number of workflows to 10 or so
                .map((workflow) => flowResult(this.getJobs(workflow.id)).then((jobs) => jobsIndex.set(workflow.id, jobs.toSorted((a, b) => a.createdAt.getTime() - b.createdAt.getTime())))));
            this.workflowStates = workflows.map((workflow) => new WorkflowState(this.editorStore, this, workflow, jobsIndex.get(workflow.id)));
            this.fetchWorkflowsState.pass();
        }
        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.fetchWorkflowsState.fail();
        }
    }
}
export class WorkspaceWorkflowManagerState extends WorkflowManagerState {
    *getJobs(workflowId) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJobs(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowId, undefined, undefined, undefined)).map((v) => WorkflowJob.serialization.fromJson(v));
    }
    *getJob(job) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, job)).map((v) => WorkflowJob.serialization.fromJson(v));
    }
    *getWorkflows() {
        return (yield this.editorStore.sdlcServerClient.getWorkflows(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, undefined, undefined, undefined)).map((v) => Workflow.serialization.fromJson(v));
    }
    *getWorkflow(workflowId) {
        return Workflow.serialization.fromJson((yield this.editorStore.sdlcServerClient.getWorkflow(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowId)));
    }
    *retryJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.retryWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
    }
    *runManualJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.runManualWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
    }
    *cancelJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.cancelWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
    }
    *getJobLogs(workflowJob) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJobLogs(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
    }
}
export class ProjectVersionWorkflowManagerState extends WorkflowManagerState {
    version;
    constructor(editorStore, sdlcState, version) {
        super(editorStore, sdlcState);
        this.version = version;
    }
    *getJobs(workflowId) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJobsByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowId, undefined, undefined, undefined)).map((v) => WorkflowJob.serialization.fromJson(v));
    }
    *getJob(job) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, job)).map((v) => WorkflowJob.serialization.fromJson(v));
    }
    *getWorkflows() {
        return (yield this.editorStore.sdlcServerClient.getWorkflowsByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, undefined, undefined, undefined)).map((v) => Workflow.serialization.fromJson(v));
    }
    *getWorkflow(workflowId) {
        return Workflow.serialization.fromJson((yield this.editorStore.sdlcServerClient.getWorkflowByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowId)));
    }
    *retryJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.retryWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
    }
    *runManualJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.runManualWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
    }
    *cancelJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.cancelWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
    }
    *getJobLogs(workflowJob) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJobLogsByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
    }
}
export class ProjectWorkflowManagerState extends WorkflowManagerState {
    *getJobs(workflowId) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJobs(this.sdlcState.activeProject.projectId, undefined, workflowId, undefined, undefined, undefined)).map((v) => WorkflowJob.serialization.fromJson(v));
    }
    *getJob(job) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJob(this.sdlcState.activeProject.projectId, undefined, job)).map((v) => WorkflowJob.serialization.fromJson(v));
    }
    *getWorkflows() {
        return (yield this.editorStore.sdlcServerClient.getWorkflows(this.sdlcState.activeProject.projectId, undefined, undefined, undefined, undefined)).map((v) => Workflow.serialization.fromJson(v));
    }
    *getWorkflow(workflowId) {
        return Workflow.serialization.fromJson((yield this.editorStore.sdlcServerClient.getWorkflow(this.sdlcState.activeProject.projectId, undefined, workflowId)));
    }
    *retryJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.retryWorkflowJob(this.sdlcState.activeProject.projectId, undefined, workflowJob));
    }
    *runManualJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.runManualWorkflowJob(this.sdlcState.activeProject.projectId, undefined, workflowJob));
    }
    *cancelJob(workflowJob) {
        (yield this.editorStore.sdlcServerClient.cancelWorkflowJob(this.sdlcState.activeProject.projectId, undefined, workflowJob));
    }
    *getJobLogs(workflowJob) {
        return (yield this.editorStore.sdlcServerClient.getWorkflowJobLogs(this.sdlcState.activeProject.projectId, undefined, workflowJob));
    }
}
//# sourceMappingURL=WorkflowManagerState.js.map