/**
 * 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 {
  type NavigationZone,
  DEFAULT_TAB_SIZE,
  EXTERNAL_APPLICATION_NAVIGATION__generateStudioProjectViewUrl,
  EXTERNAL_APPLICATION_NAVIGATION__generateNewDataCubeUrl,
  EXTERNAL_APPLICATION_NAVIGATION__generateRegistryLineageUrl,
  EXTERNAL_APPLICATION_NAVIGATION__generateRegistryGovernanceUrl,
} from '@finos/legend-application';
import {
  resolveVersion,
  retrieveProjectEntitiesWithDependencies,
  StoreProjectData,
  VersionedProjectData,
} from '@finos/legend-server-depot';
import { action, flow, flowResult, makeObservable, observable } from 'mobx';
import type { LegendMarketplaceBaseStore } from '../LegendMarketplaceBaseStore.js';
import {
  type GeneratorFn,
  type PlainObject,
  ActionState,
  assertErrorThrown,
  guaranteeNonNullable,
  guaranteeType,
  stringifyQueryParams,
  addQueryParametersToUrl,
} from '@finos/legend-shared';
import {
  type Class,
  type TDSExecutionResult,
  type TDSRowDataType,
  type V1_EntitlementsDataProductDetailsResponse,
  type V1_Terminal,
  getRowDataFromExecutionResult,
  GraphDataWithOrigin,
  GraphManagerState,
  LegendSDLC,
  V1_AdHocDeploymentDataProductOrigin,
  V1_DataProduct,
  V1_DataProductArtifact,
  V1_dataProductModelSchema,
  V1_entitlementsDataProductDetailsResponseToDataProductDetails,
  V1_PureGraphManager,
  V1_SdlcDeploymentDataProductOrigin,
  V1_TerminalModelSchema,
} from '@finos/legend-graph';
import { buildGraphForDataProduct } from '../../utils/LakehouseUtils.js';
import {
  type Entity,
  type StoredFileGeneration,
  parseGAVCoordinates,
  parseProjectIdentifier,
} from '@finos/legend-storage';
import { deserialize } from 'serializr';
import {
  EXTERNAL_APPLICATION_NAVIGATION__generateDataProductModelQueryUrl,
  EXTERNAL_APPLICATION_NAVIGATION__generateDataProductSampleQueryUrl,
  EXTERNAL_APPLICATION_NAVIGATION__generateDataSpaceQueryEditorUrl,
  EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCProjectViewUrl,
  generateLakehouseDataProductPath,
  generateContractPagePath,
  generatePermitDataAccessRequestPagePath,
} from '../../__lib__/LegendMarketplaceNavigation.js';
import {
  DataSpaceViewerState,
  EXTERNAL_APPLICATION_NAVIGATION__generateServiceQueryCreatorUrl,
} from '@finos/legend-extension-dsl-data-space/application';
import {
  type DataSpaceAnalysisResult,
  DSL_DataSpace_getGraphManagerExtension,
  retrieveAnalyticsResultCache,
} from '@finos/legend-extension-dsl-data-space/graph';
import {
  DataProductDataAccessState,
  DataProductViewerState,
  TerminalProductDataAccessState,
  TerminalProductLayoutState,
  TerminalProductViewerState,
  DATAPRODUCT_TYPE,
  getDataProductFromDetails,
} from '@finos/legend-extension-dsl-data-product';
import { LegendMarketplaceTelemetryHelper } from '../../__lib__/LegendMarketplaceTelemetryHelper.js';

const ARTIFACT_GENERATION_DATA_PRODUCT_KEY = 'dataProduct';

export class LegendMarketplaceProductViewerStore {
  readonly marketplaceBaseStore: LegendMarketplaceBaseStore;
  dataProductViewer: DataProductViewerState | undefined;
  dataProductDataAccess: DataProductDataAccessState | undefined;
  terminalProductViewer: TerminalProductViewerState | undefined;
  terminalProductDataAccess: TerminalProductDataAccessState | undefined;
  legacyDataProductViewer: DataSpaceViewerState | undefined;

  readonly loadingProductState = ActionState.create();

  constructor(marketplaceBaseStore: LegendMarketplaceBaseStore) {
    this.marketplaceBaseStore = marketplaceBaseStore;

    makeObservable(this, {
      dataProductViewer: observable,
      dataProductDataAccess: observable,
      terminalProductViewer: observable,
      legacyDataProductViewer: observable,
      setDataProductViewer: action,
      setDataProductDataAccess: action,
      setTerminalProductDataAccess: action,
      setTerminalProductViewer: action,
      setLegacyDataProductViewer: action,
      initWithProduct: flow,
      initWithTerminal: flow,
      initWithSDLCProduct: flow,
      initWithLegacyProduct: flow,
    });
  }

  setDataProductViewer(val: DataProductViewerState | undefined): void {
    this.dataProductViewer = val;
  }

  setDataProductDataAccess(val: DataProductDataAccessState | undefined): void {
    this.dataProductDataAccess = val;
  }

  setTerminalProductViewer(val: TerminalProductViewerState | undefined): void {
    this.terminalProductViewer = val;
  }
  setTerminalProductDataAccess(
    val: TerminalProductDataAccessState | undefined,
  ): void {
    this.terminalProductDataAccess = val;
  }

  setLegacyDataProductViewer(val: DataSpaceViewerState | undefined): void {
    this.legacyDataProductViewer = val;
  }

  *initWithTerminal(terminalId: string): GeneratorFn<void> {
    try {
      this.loadingProductState.inProgress();
      const rawTerminalResponse: TDSExecutionResult =
        (yield this.marketplaceBaseStore.engineServerClient.getTerminalById(
          terminalId,
        )) as TDSExecutionResult;
      const terminalRowData: TDSRowDataType[] =
        getRowDataFromExecutionResult(rawTerminalResponse);
      const matchingRows = terminalRowData.filter(
        (row: TDSRowDataType) => row.id === Number(terminalId),
      );
      const terminalProducts: V1_Terminal[] = matchingRows.map(
        (rowData: TDSRowDataType) =>
          deserialize(V1_TerminalModelSchema, rowData),
      );

      const terminalProductViewerState = new TerminalProductViewerState(
        this.marketplaceBaseStore.applicationStore,
        guaranteeNonNullable(
          terminalProducts[0],
          `No terminal found with ID ${terminalId}`,
        ),
        new TerminalProductLayoutState(),
      );

      const terminalProductDataAccessState = new TerminalProductDataAccessState(
        terminalProductViewerState,
        this.marketplaceBaseStore.terminalAccessServerClient,
        this.marketplaceBaseStore.applicationStore,
        this.marketplaceBaseStore.userSearchService,
      );

      this.setTerminalProductViewer(terminalProductViewerState);
      this.setTerminalProductDataAccess(terminalProductDataAccessState);

      this.loadingProductState.complete();
      LegendMarketplaceTelemetryHelper.logEvent_LoadTerminal(
        this.marketplaceBaseStore.applicationStore.telemetryService,
        terminalId,
        undefined,
      );
    } catch (error) {
      assertErrorThrown(error);
      const message = `Unable to load terminal ${terminalId}: ${error.message}`;
      this.marketplaceBaseStore.applicationStore.notificationService.notifyError(
        message,
      );
      this.loadingProductState.fail();
      LegendMarketplaceTelemetryHelper.logEvent_LoadTerminal(
        this.marketplaceBaseStore.applicationStore.telemetryService,
        terminalId,
        message,
      );
    }
  }

  *initWithProduct(
    dataProductId: string,
    deploymentId: number,
    tokenProvider: () => string | undefined,
  ): GeneratorFn<void> {
    try {
      this.loadingProductState.inProgress();

      const rawResponse =
        (yield this.marketplaceBaseStore.lakehouseContractServerClient.getDataProductByIdAndDID(
          dataProductId,
          deploymentId,
          tokenProvider(),
        )) as PlainObject<V1_EntitlementsDataProductDetailsResponse>;
      const fetchedDataProductDetails =
        V1_entitlementsDataProductDetailsResponseToDataProductDetails(
          rawResponse,
        );
      if (fetchedDataProductDetails.length === 0) {
        throw new Error(
          `No data products found for ID ${dataProductId} and DID ${deploymentId}`,
        );
      } else if (fetchedDataProductDetails.length > 1) {
        throw new Error(
          `Multiple data products found for ID ${dataProductId} and DID ${deploymentId}`,
        );
      }

      const entitlementsDataProductDetails = guaranteeNonNullable(
        fetchedDataProductDetails[0],
      );

      // Create graph manager state
      const graphManagerState = new GraphManagerState(
        this.marketplaceBaseStore.applicationStore.pluginManager,
        this.marketplaceBaseStore.applicationStore.logService,
      );
      const graphManager = guaranteeType(
        graphManagerState.graphManager,
        V1_PureGraphManager,
        'GraphManager must be a V1_PureGraphManager',
      );
      yield graphManager.initialize(
        {
          env: this.marketplaceBaseStore.applicationStore.config.env,
          tabSize: DEFAULT_TAB_SIZE,
          clientConfig: {
            baseUrl:
              this.marketplaceBaseStore.applicationStore.config.engineServerUrl,
          },
        },
        { engine: this.marketplaceBaseStore.remoteEngine },
      );
      yield graphManagerState.initializeSystem();

      // For AdHoc DataProducts, we need to build the graph so that we have the
      // PCMD to use for fetching access point relation types.
      if (
        entitlementsDataProductDetails.origin instanceof
        V1_AdHocDeploymentDataProductOrigin
      ) {
        yield buildGraphForDataProduct(
          entitlementsDataProductDetails,
          graphManagerState,
          graphManager,
          this.marketplaceBaseStore,
        );
      }

      const v1DataProduct = guaranteeType(
        yield getDataProductFromDetails(
          entitlementsDataProductDetails,
          graphManager,
          this.marketplaceBaseStore.depotServerClient,
        ),
        V1_DataProduct,
        `Unable to get V1_DataProduct from details for id: ${entitlementsDataProductDetails.id}`,
      );

      const projectGAV =
        entitlementsDataProductDetails.origin instanceof
        V1_SdlcDeploymentDataProductOrigin
          ? {
              groupId: entitlementsDataProductDetails.origin.group,
              artifactId: entitlementsDataProductDetails.origin.artifact,
              versionId: entitlementsDataProductDetails.origin.version,
            }
          : undefined;

      const dataProductViewerState = new DataProductViewerState(
        v1DataProduct,
        this.marketplaceBaseStore.applicationStore,
        this.marketplaceBaseStore.engineServerClient,
        this.marketplaceBaseStore.depotServerClient,
        graphManagerState,
        this.marketplaceBaseStore.applicationStore.config.options.dataProductConfig,
        this.marketplaceBaseStore.userSearchService,
        projectGAV,
        {
          viewDataProductSource: () => {
            if (
              entitlementsDataProductDetails.origin instanceof
              V1_SdlcDeploymentDataProductOrigin
            ) {
              this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                EXTERNAL_APPLICATION_NAVIGATION__generateStudioProjectViewUrl(
                  this.marketplaceBaseStore.applicationStore.config
                    .studioApplicationUrl,
                  entitlementsDataProductDetails.origin.group,
                  entitlementsDataProductDetails.origin.artifact,
                  entitlementsDataProductDetails.origin.version,
                  v1DataProduct.path,
                ),
              );
            }
          },
          openPowerBi: (apg) => {
            if (
              entitlementsDataProductDetails.origin instanceof
              V1_SdlcDeploymentDataProductOrigin
            ) {
              const {
                group: groupId,
                artifact: artifactId,
                version: versionId,
              } = entitlementsDataProductDetails.origin;
              const path = v1DataProduct.path;
              const powerBiUrl =
                this.marketplaceBaseStore.applicationStore.config.powerBiUrl;
              this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                addQueryParametersToUrl(
                  powerBiUrl,
                  stringifyQueryParams({
                    groupId,
                    artifactId,
                    versionId,
                    path,
                    apg,
                  }),
                ),
              );
            }
          },
          openDataCube: (sourceData) => {
            this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
              EXTERNAL_APPLICATION_NAVIGATION__generateNewDataCubeUrl(
                this.marketplaceBaseStore.applicationStore.config
                  .datacubeApplicationUrl,
                sourceData,
              ),
            );
          },
          openLineage: this.marketplaceBaseStore.applicationStore.config
            .registryUrl
            ? (dataProductName: string, accessPointName: string) => {
                if (
                  this.marketplaceBaseStore.applicationStore.config.registryUrl
                ) {
                  this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                    EXTERNAL_APPLICATION_NAVIGATION__generateRegistryLineageUrl(
                      this.marketplaceBaseStore.applicationStore.config
                        .registryUrl,
                      dataProductName,
                      accessPointName,
                    ),
                  );
                }
              }
            : undefined,
          openGovernance: this.marketplaceBaseStore.applicationStore.config
            .registryUrl
            ? (id: string) => {
                if (
                  this.marketplaceBaseStore.applicationStore.config.registryUrl
                ) {
                  this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                    EXTERNAL_APPLICATION_NAVIGATION__generateRegistryGovernanceUrl(
                      this.marketplaceBaseStore.applicationStore.config
                        .registryUrl,
                      id,
                    ),
                  );
                }
              }
            : undefined,
          // TODO: improve with class opening
          openQuery: (gav, type, id) => {
            if (id) {
              this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                EXTERNAL_APPLICATION_NAVIGATION__generateDataProductModelQueryUrl(
                  this.marketplaceBaseStore.applicationStore.config
                    .queryApplicationUrl,
                  gav.groupId,
                  gav.artifactId,
                  gav.versionId,
                  type,
                  v1DataProduct.path,
                  id,
                ),
              );
            }
          },
          openSampleQuery: projectGAV
            ? (sampleQueryId: string): void => {
                this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                  EXTERNAL_APPLICATION_NAVIGATION__generateDataProductSampleQueryUrl(
                    this.marketplaceBaseStore.applicationStore.config
                      .queryApplicationUrl,
                    projectGAV.groupId,
                    projectGAV.artifactId,
                    projectGAV.versionId,
                    v1DataProduct.path,
                    sampleQueryId,
                  ),
                );
              }
            : undefined,
        },
        this.marketplaceBaseStore.registryServerClient,
      );
      const dataProductDataAccessState = new DataProductDataAccessState(
        entitlementsDataProductDetails,
        dataProductViewerState,
        this.marketplaceBaseStore.lakehouseContractServerClient,
        this.marketplaceBaseStore.lakehousePlatformServerClient,
        this.marketplaceBaseStore.lakehouseIngestServerClient,
        this.marketplaceBaseStore.applicationStore.pluginManager.getApplicationPlugins(),
        {
          getContractTaskUrl: (contractId: string, taskId: string) =>
            this.marketplaceBaseStore.applicationStore.navigationService.navigator.generateAddress(
              generateContractPagePath(contractId, taskId),
            ),
          getDataProductUrl: (_dataProductId: string, _deploymentId: number) =>
            this.marketplaceBaseStore.applicationStore.navigationService.navigator.generateAddress(
              generateLakehouseDataProductPath(_dataProductId, _deploymentId),
            ),
          getTaskPageUrl: (id: string) =>
            this.marketplaceBaseStore.applicationStore.navigationService.navigator.generateAddress(
              generatePermitDataAccessRequestPagePath(id),
            ),
        },
        this.marketplaceBaseStore.permitWorkflowServerClient,
      );
      this.setDataProductViewer(dataProductViewerState);
      this.setDataProductDataAccess(dataProductDataAccessState);
      dataProductViewerState.legendAIConfig =
        this.marketplaceBaseStore.applicationStore.config.legendAIConfig;
      dataProductViewerState.init(entitlementsDataProductDetails);
      dataProductDataAccessState.init(tokenProvider);
      this.loadingProductState.complete();
      const origin =
        entitlementsDataProductDetails.origin instanceof
        V1_SdlcDeploymentDataProductOrigin
          ? {
              type: DATAPRODUCT_TYPE.SDLC,
              groupId: entitlementsDataProductDetails.origin.group,
              artifactId: entitlementsDataProductDetails.origin.artifact,
              versionId: entitlementsDataProductDetails.origin.version,
            }
          : {
              type: DATAPRODUCT_TYPE.ADHOC,
            };
      LegendMarketplaceTelemetryHelper.logEvent_LoadDataProduct(
        this.marketplaceBaseStore.applicationStore.telemetryService,
        {
          origin: origin,
          dataProductId: dataProductId,
          deploymentId: deploymentId,
          name: entitlementsDataProductDetails.dataProduct.name,
          environmentClassification:
            entitlementsDataProductDetails.lakehouseEnvironment?.type,
        },
        undefined,
      );
    } catch (error) {
      assertErrorThrown(error);
      const message = `Unable to load product ${dataProductId}: ${error.message}`;
      this.marketplaceBaseStore.applicationStore.notificationService.notifyError(
        message,
      );
      this.loadingProductState.fail();
      LegendMarketplaceTelemetryHelper.logEvent_LoadDataProduct(
        this.marketplaceBaseStore.applicationStore.telemetryService,
        {
          dataProductId: dataProductId,
          deploymentId: deploymentId,
        },
        message,
      );
    }
  }

  /**
   * This is a fallback to support the old data product URL with GAV and path.
   * We check to see if the data product has been deployed, and if so, we redirect
   * to the new URL format with data product ID and deployment ID. If no deploymentId
   * exists (meaning nativeModelAccess only, no access point groups), we render the
   * nativeModelAccess UI directly.
   *
   * @param gav The GAV coordinates of the product.
   * @param path The path to the product.
   */
  *initWithSDLCProduct(gav: string, path: string): GeneratorFn<void> {
    try {
      this.loadingProductState.inProgress();
      const projectData = VersionedProjectData.serialization.fromJson(
        parseGAVCoordinates(gav) as unknown as PlainObject,
      );
      try {
        const storeProject = new StoreProjectData();
        storeProject.groupId = projectData.groupId;
        storeProject.artifactId = projectData.artifactId;
        const entityResponse =
          (yield this.marketplaceBaseStore.depotServerClient.getVersionEntity(
            projectData.groupId,
            projectData.artifactId,
            resolveVersion(projectData.versionId),
            path,
          )) as Entity;
        const schema = V1_dataProductModelSchema(
          this.marketplaceBaseStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins(),
        );
        const v1DataProduct = deserialize(schema, entityResponse.content);
        const files =
          (yield this.marketplaceBaseStore.depotServerClient.getGenerationFilesByType(
            storeProject,
            resolveVersion(projectData.versionId),
            ARTIFACT_GENERATION_DATA_PRODUCT_KEY,
          )) as StoredFileGeneration[];
        const fileGen = files.filter((e) => e.path === v1DataProduct.path)[0]
          ?.file.content;
        if (fileGen) {
          const content: PlainObject = JSON.parse(fileGen) as PlainObject;
          const dataProductInfo = content.dataProduct as
            | { deploymentId?: string }
            | undefined;

          if (dataProductInfo?.deploymentId) {
            const dataProductId = v1DataProduct.name.toUpperCase();
            const deploymentId = Number(dataProductInfo.deploymentId);
            this.marketplaceBaseStore.applicationStore.navigationService.navigator.goToLocation(
              generateLakehouseDataProductPath(dataProductId, deploymentId),
            );
            this.loadingProductState.complete();
            LegendMarketplaceTelemetryHelper.logEvent_LoadSDLCDataProduct(
              this.marketplaceBaseStore.applicationStore.telemetryService,
              {
                origin: {
                  type: DATAPRODUCT_TYPE.SDLC,
                  groupId: projectData.groupId,
                  artifactId: projectData.artifactId,
                  versionId: projectData.versionId,
                  path: path,
                },
                dataProductId: dataProductId,
                deploymentId: deploymentId,
              },
              undefined,
            );
          } else {
            const graphManagerState = new GraphManagerState(
              this.marketplaceBaseStore.applicationStore.pluginManager,
              this.marketplaceBaseStore.applicationStore.logService,
            );
            const graphManager = guaranteeType(
              graphManagerState.graphManager,
              V1_PureGraphManager,
              'GraphManager must be a V1_PureGraphManager',
            );

            yield graphManager.initialize(
              {
                env: this.marketplaceBaseStore.applicationStore.config.env,
                tabSize: DEFAULT_TAB_SIZE,
                clientConfig: {
                  baseUrl:
                    this.marketplaceBaseStore.applicationStore.config
                      .engineServerUrl,
                  queryBaseUrl:
                    this.marketplaceBaseStore.applicationStore.config
                      .engineQueryServerUrl,
                  enableCompression: true,
                },
              },
              { engine: this.marketplaceBaseStore.remoteEngine },
            );
            yield graphManagerState.initializeSystem();

            graphManagerState.graph.setOrigin(
              new LegendSDLC(
                projectData.groupId,
                projectData.artifactId,
                resolveVersion(projectData.versionId),
              ),
            );

            const dataProductArtifact =
              V1_DataProductArtifact.serialization.fromJson(content);

            const projectGAV = {
              groupId: projectData.groupId,
              artifactId: projectData.artifactId,
              versionId: projectData.versionId,
            };

            const dataProductViewerState = new DataProductViewerState(
              v1DataProduct,
              this.marketplaceBaseStore.applicationStore,
              this.marketplaceBaseStore.engineServerClient,
              this.marketplaceBaseStore.depotServerClient,
              graphManagerState,
              this.marketplaceBaseStore.applicationStore.config.options.dataProductConfig,
              this.marketplaceBaseStore.userSearchService,
              projectGAV,
              {
                viewDataProductSource: () => {
                  this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                    EXTERNAL_APPLICATION_NAVIGATION__generateStudioProjectViewUrl(
                      this.marketplaceBaseStore.applicationStore.config
                        .studioApplicationUrl,
                      projectData.groupId,
                      projectData.artifactId,
                      projectData.versionId,
                      v1DataProduct.path,
                    ),
                  );
                },
                openQuery: (_gav, type, id): void => {
                  if (id) {
                    this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                      EXTERNAL_APPLICATION_NAVIGATION__generateDataProductModelQueryUrl(
                        this.marketplaceBaseStore.applicationStore.config
                          .queryApplicationUrl,
                        _gav.groupId,
                        _gav.artifactId,
                        _gav.versionId,
                        type,
                        v1DataProduct.path,
                        id,
                      ),
                    );
                  }
                },
                openSampleQuery: (sampleQueryId: string): void => {
                  this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                    EXTERNAL_APPLICATION_NAVIGATION__generateDataProductSampleQueryUrl(
                      this.marketplaceBaseStore.applicationStore.config
                        .queryApplicationUrl,
                      projectData.groupId,
                      projectData.artifactId,
                      projectData.versionId,
                      v1DataProduct.path,
                      sampleQueryId,
                    ),
                  );
                },
              },
              this.marketplaceBaseStore.registryServerClient,
            );

            yield flowResult(
              dataProductViewerState.init(undefined, dataProductArtifact),
            );

            dataProductViewerState.legendAIConfig =
              this.marketplaceBaseStore.applicationStore.config.legendAIConfig;
            this.setDataProductViewer(dataProductViewerState);
            this.loadingProductState.complete();
            LegendMarketplaceTelemetryHelper.logEvent_LoadSDLCDataProduct(
              this.marketplaceBaseStore.applicationStore.telemetryService,
              {
                origin: {
                  type: DATAPRODUCT_TYPE.SDLC,
                  groupId: projectData.groupId,
                  artifactId: projectData.artifactId,
                  versionId: projectData.versionId,
                  path: path,
                },
              },
              undefined,
            );
          }
        } else {
          throw new Error('File generation not found');
        }
      } catch (error) {
        assertErrorThrown(error);
        const message = `Unable to load product ${path}: ${error.message}`;
        this.marketplaceBaseStore.applicationStore.notificationService.notifyError(
          message,
        );
        this.loadingProductState.fail();
        LegendMarketplaceTelemetryHelper.logEvent_LoadSDLCDataProduct(
          this.marketplaceBaseStore.applicationStore.telemetryService,
          {
            origin: {
              type: DATAPRODUCT_TYPE.SDLC,
              groupId: projectData.groupId,
              artifactId: projectData.artifactId,
              versionId: projectData.versionId,
              path: path,
            },
          },
          message,
        );
      }
    } catch (error) {
      assertErrorThrown(error);
      this.marketplaceBaseStore.applicationStore.notificationService.notifyError(
        `Unable to deserialize gav for product ${path}: ${error.message}`,
      );
      this.loadingProductState.fail();
    }
  }

  *initWithLegacyProduct(gav: string, path: string): GeneratorFn<void> {
    try {
      this.loadingProductState.inProgress();
      const { groupId, artifactId, versionId } = parseGAVCoordinates(gav);
      try {
        // create graph manager
        const graphManagerState = new GraphManagerState(
          this.marketplaceBaseStore.applicationStore.pluginManager,
          this.marketplaceBaseStore.applicationStore.logService,
        );
        const graphManager = guaranteeType(
          graphManagerState.graphManager,
          V1_PureGraphManager,
          'GraphManager must be a V1_PureGraphManager',
        );

        // initialize graph manager
        yield graphManager.initialize(
          {
            env: this.marketplaceBaseStore.applicationStore.config.env,
            tabSize: DEFAULT_TAB_SIZE,
            clientConfig: {
              baseUrl:
                this.marketplaceBaseStore.applicationStore.config
                  .engineServerUrl,
              queryBaseUrl:
                this.marketplaceBaseStore.applicationStore.config
                  .engineQueryServerUrl,
              enableCompression: true,
            },
          },
          { engine: this.marketplaceBaseStore.remoteEngine },
        );
        yield graphManagerState.initializeSystem();

        // fetch project
        this.loadingProductState.setMessage(`Fetching project...`);
        const project = StoreProjectData.serialization.fromJson(
          (yield flowResult(
            this.marketplaceBaseStore.depotServerClient.getProject(
              groupId,
              artifactId,
            ),
          )) as PlainObject<StoreProjectData>,
        );

        // set origin
        graphManagerState.graph.setOrigin(
          new LegendSDLC(groupId, artifactId, resolveVersion(versionId)),
        );

        // analyze data product
        const analysisResult = (yield DSL_DataSpace_getGraphManagerExtension(
          graphManagerState.graphManager,
        ).analyzeDataSpace(
          path,
          () =>
            retrieveProjectEntitiesWithDependencies(
              project,
              versionId,
              this.marketplaceBaseStore.depotServerClient,
            ),
          () =>
            retrieveAnalyticsResultCache(
              project,
              versionId,
              path,
              this.marketplaceBaseStore.depotServerClient,
            ),
          this.loadingProductState,
        )) as DataSpaceAnalysisResult;

        const dataSpaceViewerState = new DataSpaceViewerState(
          this.marketplaceBaseStore.applicationStore,
          graphManagerState,
          groupId,
          artifactId,
          versionId,
          analysisResult,
          {
            retrieveGraphData: () =>
              new GraphDataWithOrigin(
                new LegendSDLC(groupId, artifactId, versionId),
              ),
            queryDataSpace: (executionContextKey: string) => {
              LegendMarketplaceTelemetryHelper.logEvent_ClickQueryDataProduct(
                this.marketplaceBaseStore.applicationStore.telemetryService,
                groupId,
                artifactId,
                versionId,
                analysisResult.path,
                executionContextKey,
              );
              this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                EXTERNAL_APPLICATION_NAVIGATION__generateDataSpaceQueryEditorUrl(
                  this.marketplaceBaseStore.applicationStore.config
                    .queryApplicationUrl,
                  groupId,
                  artifactId,
                  versionId,
                  analysisResult.path,
                  executionContextKey,
                  undefined,
                  undefined,
                ),
              );
            },
            viewProject: (entityPath: string | undefined) => {
              this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                EXTERNAL_APPLICATION_NAVIGATION__generateStudioProjectViewUrl(
                  this.marketplaceBaseStore.applicationStore.config
                    .studioApplicationUrl,
                  groupId,
                  artifactId,
                  versionId,
                  entityPath,
                ),
              );
            },
            viewSDLCProject: async (entityPath: string | undefined) => {
              // find the matching SDLC instance
              const projectIDPrefix = parseProjectIdentifier(
                project.projectId,
              ).prefix;
              const matchingSDLCEntry =
                this.marketplaceBaseStore.applicationStore.config.studioInstances.find(
                  (entry) => entry.sdlcProjectIDPrefix === projectIDPrefix,
                );
              if (matchingSDLCEntry) {
                this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                  EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCProjectViewUrl(
                    matchingSDLCEntry.url,
                    project.projectId,
                    entityPath,
                  ),
                );
              } else {
                this.marketplaceBaseStore.applicationStore.notificationService.notifyWarning(
                  `Can't find the corresponding SDLC instance to view the SDLC project`,
                );
              }
            },
            queryClass: (_class: Class): void => {
              this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                EXTERNAL_APPLICATION_NAVIGATION__generateDataSpaceQueryEditorUrl(
                  this.marketplaceBaseStore.applicationStore.config
                    .queryApplicationUrl,
                  groupId,
                  artifactId,
                  versionId,
                  analysisResult.path,
                  analysisResult.defaultExecutionContext.name,
                  undefined,
                  _class.path,
                ),
              );
            },
            openServiceQuery: (servicePath: string): void => {
              LegendMarketplaceTelemetryHelper.logEvent_ClickOpenServiceQuery(
                this.marketplaceBaseStore.applicationStore.telemetryService,
                groupId,
                artifactId,
                versionId,
                servicePath,
              );
              this.marketplaceBaseStore.applicationStore.navigationService.navigator.visitAddress(
                EXTERNAL_APPLICATION_NAVIGATION__generateServiceQueryCreatorUrl(
                  this.marketplaceBaseStore.applicationStore.config
                    .queryApplicationUrl,
                  groupId,
                  artifactId,
                  versionId,
                  servicePath,
                ),
              );
            },
            onZoneChange: (zone: NavigationZone | undefined): void => {
              if (zone === undefined) {
                this.marketplaceBaseStore.applicationStore.navigationService.navigator.resetZone();
              } else {
                this.marketplaceBaseStore.applicationStore.navigationService.navigator.updateCurrentZone(
                  zone,
                );
              }
            },
            onQuickStartTabChange: (
              tabKey: string,
              executableTitle: string,
            ): void => {
              LegendMarketplaceTelemetryHelper.logEvent_ClickQuickStartExtensionTab(
                this.marketplaceBaseStore.applicationStore.telemetryService,
                groupId,
                artifactId,
                versionId,
                analysisResult.path,
                tabKey,
                executableTitle,
              );
            },
          },
        );
        dataSpaceViewerState.legendAIConfig =
          this.marketplaceBaseStore.applicationStore.config.legendAIConfig;
        this.setLegacyDataProductViewer(dataSpaceViewerState);
        this.loadingProductState.complete();
        LegendMarketplaceTelemetryHelper.logEvent_LoadLegacyDataProduct(
          this.marketplaceBaseStore.applicationStore.telemetryService,
          groupId,
          artifactId,
          versionId,
          path,
          undefined,
        );
      } catch (error) {
        assertErrorThrown(error);
        const message = `Unable to load legacy data product ${gav}::${path}: ${error.message}`;
        this.marketplaceBaseStore.applicationStore.notificationService.notifyError(
          message,
        );
        this.loadingProductState.fail();
        LegendMarketplaceTelemetryHelper.logEvent_LoadLegacyDataProduct(
          this.marketplaceBaseStore.applicationStore.telemetryService,
          groupId,
          artifactId,
          versionId,
          path,
          message,
        );
      }
    } catch (error) {
      assertErrorThrown(error);
      this.marketplaceBaseStore.applicationStore.notificationService.notifyError(
        `Unable to parse gav for product ${path}: ${error.message}`,
      );
      this.loadingProductState.fail();
    }
  }
}
