/**
 * Copyright (c) 2025-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 { TelemetryService } from '@finos/legend-application';
import {
  type V1_LiteDataContract,
  V1_ResourceType,
  type V1_ContractUserEventRecord,
  type V1_EntitlementsLakehouseEnvironmentType,
} from '@finos/legend-graph';
import { LEGEND_MARKETPLACE_APP_EVENT } from './LegendMarketplaceAppEvent.js';
import { uuid } from '@finos/legend-shared';
import {
  SEARCH_SESSION_KEY,
  TELEMETRY_EVENT_STATUS,
  type DATAPRODUCT_TYPE,
  type MarketplaceUserSession,
} from '@finos/legend-extension-dsl-data-product';

export enum LEGEND_MARKETPLACE_PAGE {
  HOME_PAGE = 'Home Page',
  SEARCH_RESULTS_PAGE = 'Search Results Page',
}

export enum CONTRACT_ACTION {
  APPROVED = 'approved',
  DENIED = 'denied',
}

export enum ICON_TOOLBAR_TYPE {
  USER = 'User Icon',
  HELP = 'Help Icon',
}

type MarketplaceDataProductOrigin_TelemetryData = {
  type: DATAPRODUCT_TYPE;
  groupId?: string | undefined;
  artifactId?: string | undefined;
  versionId?: string | undefined;
  path?: string | undefined;
};

type MarketplaceDataProduct_TelemetryData = {
  origin?: MarketplaceDataProductOrigin_TelemetryData | undefined;
  dataProductId?: string | undefined;
  deploymentId?: number | undefined;
  name?: string | undefined;
  environmentClassification?:
    | V1_EntitlementsLakehouseEnvironmentType
    | undefined;
};

export class LegendMarketplaceTelemetryHelper {
  private static getOrCreateUserSession(): MarketplaceUserSession {
    const stored = localStorage.getItem(SEARCH_SESSION_KEY);

    if (stored) {
      const session = JSON.parse(stored) as MarketplaceUserSession;
      return session;
    } else {
      const initialSession: MarketplaceUserSession = {
        eventId: 0,
        searchSessionId: undefined,
      };
      localStorage.setItem(SEARCH_SESSION_KEY, JSON.stringify(initialSession));
      return initialSession;
    }
  }

  private static updateSearchSessionId(
    searchSessionId: string,
  ): MarketplaceUserSession {
    const currentSession = this.getOrCreateUserSession();
    const newSearchSession: MarketplaceUserSession = {
      ...currentSession,
      searchSessionId: searchSessionId,
    };

    localStorage.setItem(SEARCH_SESSION_KEY, JSON.stringify(newSearchSession));
    return newSearchSession;
  }

  static clearSearchSessionId(): MarketplaceUserSession {
    const currentSession = this.getOrCreateUserSession();
    const newSearchSession: MarketplaceUserSession = {
      ...currentSession,
      searchSessionId: undefined,
    };

    if (currentSession.eventId !== 0) {
      localStorage.setItem(
        SEARCH_SESSION_KEY,
        JSON.stringify(newSearchSession),
      );
    }
    return newSearchSession;
  }

  private static updateEventId() {
    const currentSession = this.getOrCreateUserSession();
    const updatedSession: MarketplaceUserSession = {
      ...currentSession,
      eventId: currentSession.eventId + 1,
    };

    localStorage.setItem(SEARCH_SESSION_KEY, JSON.stringify(updatedSession));
    return updatedSession;
  }

  static logEvent_ClickingDataProductCard(
    telemetryService: TelemetryService,
    dataProductData: MarketplaceDataProduct_TelemetryData,
    clickedFrom: LEGEND_MARKETPLACE_PAGE,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.CLICK_DATA_PRODUCT_CARD,
      {
        ...dataProductData,
        clickedFrom: clickedFrom,
        ...session,
      },
    );
  }

  static logEvent_SearchQuery(
    telemetryService: TelemetryService,
    query: string | undefined,
    useProducerSearch: boolean,
    searchedFrom: LEGEND_MARKETPLACE_PAGE,
    useFieldSearch: boolean = false,
  ): void {
    this.updateSearchSessionId(uuid());
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.SEARCH_QUERY, {
      query,
      useProducerSearch,
      useFieldSearch,
      searchedFrom,
      ...session,
    });
  }

  static logEvent_ActionDataContracts(
    telemetryService: TelemetryService,
    selectedContracts: V1_ContractUserEventRecord[],
    pendingTaskContracts: V1_LiteDataContract[] | undefined,
    action: CONTRACT_ACTION,
    actionTakenBy: string,
    errors: string[] | undefined,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    const actionedContractsDetails = selectedContracts.map((contract) => {
      const dataContract = pendingTaskContracts?.find(
        (c) => c.guid === contract.dataContractId,
      );
      const accessPointGroup =
        dataContract?.resourceType === V1_ResourceType.ACCESS_POINT_GROUP
          ? dataContract.accessPointGroup
          : `${dataContract?.accessPointGroup ?? 'Unknown'} (${dataContract?.resourceType ?? 'Unknown Type'})`;
      return {
        taskId: contract.taskId,
        dataContractId: contract.dataContractId,
        consumer: contract.consumer,
        type: contract.type,
        targetDataProduct: dataContract?.resourceId ?? 'Unknown',
        targetAccessPointGroup: accessPointGroup ?? 'Unknown',
        requester: dataContract?.createdBy,
        ...session,
      };
    });
    const data =
      errors === undefined
        ? {
            actionedContractsDetails: actionedContractsDetails,
            action: action,
            actionTakenBy: actionTakenBy,
            status: TELEMETRY_EVENT_STATUS.SUCCESS,
          }
        : {
            actionedContractsDetails: actionedContractsDetails,
            action: action,
            actionTakenBy: actionTakenBy,
            status: TELEMETRY_EVENT_STATUS.FAILURE,
            errors: errors,
          };
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.ACTION_DATA_CONTRACTS,
      data,
    );
  }

  static logEvent_LoadDataProduct(
    telemetryService: TelemetryService,
    dataProductData: MarketplaceDataProduct_TelemetryData,
    error: string | undefined,
  ): void {
    const telemetryData =
      error === undefined
        ? { ...dataProductData, status: TELEMETRY_EVENT_STATUS.SUCCESS }
        : {
            ...dataProductData,
            status: TELEMETRY_EVENT_STATUS.FAILURE,
            error: error,
          };
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.LOAD_DATA_PRODUCT,
      telemetryData,
    );
  }

  static logEvent_LoadSDLCDataProduct(
    telemetryService: TelemetryService,
    dataProductData: MarketplaceDataProduct_TelemetryData,
    error: string | undefined,
  ): void {
    const telemetryData =
      error === undefined
        ? { ...dataProductData, status: TELEMETRY_EVENT_STATUS.SUCCESS }
        : {
            ...dataProductData,
            status: TELEMETRY_EVENT_STATUS.FAILURE,
            error: error,
          };
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.LOAD_SDLC_DATA_PRODUCT,
      telemetryData,
    );
  }

  static logEvent_LoadTerminal(
    telemetryService: TelemetryService,
    terminalId: string,
    error: string | undefined,
  ): void {
    const telemetryData =
      error === undefined
        ? { terminalId: terminalId, status: TELEMETRY_EVENT_STATUS.SUCCESS }
        : {
            terminalId: terminalId,
            status: TELEMETRY_EVENT_STATUS.FAILURE,
            error: error,
          };
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.LOAD_TERMINAL,
      telemetryData,
    );
  }

  static logEvent_LoadLegacyDataProduct(
    telemetryService: TelemetryService,
    groupId: string,
    artifactId: string,
    versionId: string,
    path: string,
    error: string | undefined,
  ): void {
    const telemetryData =
      error === undefined
        ? {
            groupId: groupId,
            artifactId: artifactId,
            versionId: versionId,
            path: path,
            status: TELEMETRY_EVENT_STATUS.SUCCESS,
          }
        : {
            groupId: groupId,
            artifactId: artifactId,
            versionId: versionId,
            path: path,
            status: TELEMETRY_EVENT_STATUS.FAILURE,
            error: error,
          };
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.LOAD_LEGACY_DATA_PRODUCT,
      telemetryData,
    );
  }

  static logEvent_ClickHeadertab(
    telemetryService: TelemetryService,
    tabTitle: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.CLICK_HEADER_TAB, {
      tabTitle: tabTitle,
      ...session,
    });
  }

  static logEvent_ToggleProducerSearch(
    telemetryService: TelemetryService,
    isEnabled: boolean,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.PRODUCER_SEARCH_TOGGLE,
      {
        toggleAction: isEnabled ? 'enabled' : 'disabled',
        ...session,
      },
    );
  }

  static logEvent_ToggleFieldSearch(
    telemetryService: TelemetryService,
    isEnabled: boolean,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.FIELD_SEARCH_TOGGLE,
      {
        toggleAction: isEnabled ? 'enabled' : 'disabled',
        ...session,
      },
    );
  }

  static logEvent_ToggleThemeMode(
    telemetryService: TelemetryService,
    isDarkMode: boolean,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.TOGGLE_THEME_MODE, {
      currentTheme: isDarkMode ? 'dark' : 'light',
      ...session,
    });
  }

  static logEvent_ToggleViewMode(
    telemetryService: TelemetryService,
    viewMode: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.TOGGLE_VIEW_MODE, {
      viewMode,
      ...session,
    });
  }

  static logEvent_ToggleServicesViewMode(
    telemetryService: TelemetryService,
    viewMode: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.TOGGLE_SERVICES_VIEW_MODE,
      {
        viewMode,
        ...session,
      },
    );
  }

  static logEvent_ClickToolbarMenu(
    telemetryService: TelemetryService,
    iconSource: ICON_TOOLBAR_TYPE,
    menuTitle: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.CLICK_TOOLBAR_MENU, {
      iconSource: iconSource,
      menuTitle: menuTitle,
      ...session,
    });
  }

  static logEvent_SearchAutosuggestSelection(
    telemetryService: TelemetryService,
    query: string,
    suggestionType: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.SEARCH_AUTOSUGGEST_SELECTION,
      {
        query: query,
        suggestionType: suggestionType,
        ...session,
      },
    );
  }

  static logEvent_DismissHomePageBanner(
    telemetryService: TelemetryService,
    bannerId: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.DISMISS_HOME_PAGE_BANNER,
      {
        bannerId: bannerId,
        ...session,
      },
    );
  }

  static logEvent_SubmitFeedback(
    telemetryService: TelemetryService,
    originPage: string,
    rating: number,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.SUBMIT_FEEDBACK, {
      originPage: originPage,
      rating: rating,
      ...session,
    });
  }

  static logEvent_ClickQueryDataProduct(
    telemetryService: TelemetryService,
    groupId: string,
    artifactId: string,
    versionId: string,
    path: string,
    executionContextKey: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.CLICK_QUERY_DATA_PRODUCT,
      {
        groupId: groupId,
        artifactId: artifactId,
        versionId: versionId,
        path: path,
        executionContextKey: executionContextKey,
        ...session,
      },
    );
  }

  static logEvent_ClickOpenServiceQuery(
    telemetryService: TelemetryService,
    groupId: string,
    artifactId: string,
    versionId: string,
    servicePath: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.CLICK_OPEN_SERVICE_QUERY,
      {
        groupId: groupId,
        artifactId: artifactId,
        versionId: versionId,
        servicePath: servicePath,
        ...session,
      },
    );
  }

  static logEvent_ClickQuickStartExtensionTab(
    telemetryService: TelemetryService,
    groupId: string,
    artifactId: string,
    versionId: string,
    path: string,
    tabKey: string,
    executableTitle: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.CLICK_QUICKSTART_EXTENSION_TAB,
      {
        groupId: groupId,
        artifactId: artifactId,
        versionId: versionId,
        path: path,
        tabKey: tabKey,
        executableTitle: executableTitle,
        ...session,
      },
    );
  }

  static logEvent_ApplySearchFilter(
    telemetryService: TelemetryService,
    filterType: string,
    filterValue: string,
    action: 'select' | 'deselect',
    searchQuery: string | undefined,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.APPLY_SEARCH_FILTER,
      {
        filterType,
        filterValue,
        action,
        searchQuery,
        ...session,
      },
    );
  }

  static logEvent_ClearSearchFilters(
    telemetryService: TelemetryService,
    searchQuery: string | undefined,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.CLEAR_SEARCH_FILTERS,
      {
        searchQuery,
        ...session,
      },
    );
  }

  static logEvent_ShowAllDataProducts(
    telemetryService: TelemetryService,
    searchQuery: string | undefined,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(
      LEGEND_MARKETPLACE_APP_EVENT.SHOW_ALL_DATA_PRODUCTS,
      {
        searchQuery,
        ...session,
      },
    );
  }

  static logEvent_SearchServices(
    telemetryService: TelemetryService,
    query: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.SEARCH_SERVICES, {
      query,
      ...session,
    });
  }

  static logEvent_SortServices(
    telemetryService: TelemetryService,
    sortValue: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.SORT_SERVICES, {
      sortValue,
      ...session,
    });
  }

  static logEvent_FilterServices(
    telemetryService: TelemetryService,
    filterType: string,
    filterValue: string,
    action: 'add' | 'remove' | 'clear',
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.FILTER_SERVICES, {
      filterType,
      filterValue,
      action,
      ...session,
    });
  }

  static logEvent_ClickServiceCard(
    telemetryService: TelemetryService,
    pattern: string,
    title: string,
  ): void {
    this.updateEventId();
    const session = this.getOrCreateUserSession();
    telemetryService.logEvent(LEGEND_MARKETPLACE_APP_EVENT.CLICK_SERVICE_CARD, {
      pattern,
      title,
      ...session,
    });
  }
}
