'use client';

/**
 * Event logger class to wrap Segment.js and make it easy to replace in the future.
 */
import { extendAnalyticsSessionId } from '@/logging/analyticsSessions';
import { eventSubscribers } from '@/logging/eventSubscribers';
import {
  getShouldLogEventsToConsole,
  logToConsole,
} from '@/logging/logWebUserEvent';
import {
  getAnonymousId,
  track as segmentTrack2025,
} from '@/logging/segmentClient';
import { Clip } from '@/state/clipStore';
import { GenerateFormStore } from '@/state/createStore';
import { SessionStore } from '@/state/sessionStore';

import { EventNames } from './event-names';

const extendSessionRetryTimes = 1;

export enum PageEventType {
  SCROLL = 'scroll',
  CLICK = 'click',
  REDIRECT = 'redirect',
  OPEN = 'open',
  CLOSE = 'close',
  VIEW = 'view',
  HOVER = 'hover',
  VISIT = 'visit',
  PROGRAMMATIC = 'programmatic',
  ENTER = 'enter',
}

export class EventLogger {
  /**
   * Whether the logger has been initialized.
   */
  public initialized = false;
  /**
   * Whether the user has been identified.
   */
  public identified = false;

  // Locks for session id creation and extension to prevent multiple requests
  public extendSessionLock = false;
  public createSessionLock = false;

  /**
   * Track an event with the segment library
   * @param event
   * @param properties
   */
  segmentTrack = async (
    event: string,
    properties?: Record<string, any>,
    session?: SessionStore
  ) => {
    if (getShouldLogEventsToConsole()) {
      logToConsole(event, properties || {});
    }
    if (typeof window !== 'undefined') {
      const seed = { deviceId: await getAnonymousId() };

      extendAnalyticsSessionId(
        properties?.userId,
        JSON.stringify(seed),
        Date.now() / 1000,
        extendSessionRetryTimes
      )
        .then((sessionId) => {
          if (sessionId && properties) {
            properties.sessionId = sessionId;
          }
          segmentTrack2025(event, properties || {});
          const propertiesWithContext = {
            email: session?.userEmail,
            phone: session?.phoneNumber,
            clerkId: session?.clerkId,
            ...properties,
          };
          // Handle async push calls for all subscribers
          if (session?.flags?.['event-subscribers']) {
            Promise.all(
              eventSubscribers?.map((subscriber) =>
                subscriber.push(event, propertiesWithContext)
              ) || []
            );
          }
        })
        .catch((error: any) => {
          console.log('Error in extending analytics session id', error);
          segmentTrack2025(event, properties || {});
        });
    }
  };

  /**
   * Log a web page event.
   * @param params - Object containing event parameters
   * @param params.userId - The user ID
   * @param params.eventType - The type of event (e.g., 'open', 'close', 'click')
   * @param params.element - The primary element involved in the event (e.g., 'banner_close_button')
   * @param params.elementIndex - Index of the element (if applicable)
   * @param params.secondaryElement - A secondary element involved in the event (if applicable)
   * @param params.secondaryElementIndex - Index of the secondary element (if applicable)
   * @param params.entityId - ID of the related entity (e.g., banner ID, popup ID)
   * @param params.entityType - Type of the related entity (e.g., 'banner', 'popup')
   * @param params.secondaryEntityId - ID of the related secondary entity (e.g., the ID of the song in a notification)
   * @param params.secondaryEntityType - Type of the related secondary entity (e.g., 'banner', 'popup')
   * @param params.secondarySessionId - ID of the optional secondary session (i.e. within a particular feature)
   * @param params.secondarySessionType - Type of the optional secondary session (e.g., 'notifications')
   * @param params.method - The method of interaction (e.g., 'button', 'auto', 'click_outside')
   * @param params.pageUrl - The URL of the page where the event occurred
   * @param params.extraProperties - Additional contextual information that doesn't neatly into another field
   *
   * TODO: Standardize the fields and their usage according to the provided table.
   * Consider removing unused fields and ensuring consistent use of new fields (method, pageUrl).
   *
   * @deprecated Use @/logging/logWebUserEvent instead.
   */
  logWebPageEvent = (params: {
    userId: string | null;
    eventType: PageEventType;
    element?: string | null;
    elementIndex?: number | null;
    secondaryElement?: string | null;
    secondaryElementIndex?: number | null;
    entityId?: string | null;
    entityType?: string | null;
    secondaryEntityId?: string | null;
    secondaryEntityType?: string | null;
    secondarySessionId?: string | null;
    secondarySessionType?: string | null;
    method?: string;
    pageUrl?: string;
    extraProperties?: Record<string, any> | null;
  }) => {
    this.segmentTrack(EventNames.webPageEvent, params);
  };

  // Adding this function with named parameters
  // will follow up to refactor + cleanup logAudioActionEvent
  logAudioActionWithContext = (audioActionProps: {
    isMobile: boolean;
    actionName: string;
    clip: Clip;
    session: SessionStore;
    pathname: string;
    playlistId?: string | null;
    componentContext?: string;
  }) => {
    const {
      isMobile,
      actionName,
      clip,
      session,
      pathname,
      playlistId,
      componentContext,
    } = audioActionProps;
    this.segmentTrack(
      EventNames.audioActionEvent,
      {
        isMobile,
        clipId: clip?.id,
        playlistId,
        isPublic: clip?.is_public,
        actionName,
        isUserSongOwner:
          session?.userId !== undefined && session?.userId === clip?.user_id,
        clickSourceUrl: pathname,
        userId: session?.userId,
        componentContext,
      },
      session
    );
  };

  logAudioActionEvent = (
    isMobile: boolean,
    actionName: string,
    clip: Clip,
    session: SessionStore,
    pathname: string,
    playlistId?: string | null
  ) => {
    this.segmentTrack(
      EventNames.audioActionEvent,
      {
        isMobile: isMobile,
        clipId: clip?.id,
        playlistId: playlistId,
        isPublic: clip?.is_public,
        actionName: actionName,
        isUserSongOwner:
          session?.userId !== undefined && session?.userId === clip?.user_id,
        clickSourceUrl: pathname,
        userId: session?.userId,
      },
      session
    );
  };

  logAudioCreationEvent = (
    isMobile: boolean,
    actionName: string,
    genForm: GenerateFormStore,
    session: SessionStore,
    extraFields?: Record<string, any>
  ) => {
    let editedClipId: string | null = null;

    if (genForm.coverClipId) {
      editedClipId = genForm.coverClipId;
    } else if (genForm.continueClipId) {
      editedClipId = genForm.continueClipId;
    } else if (genForm.personaClipId) {
      editedClipId = genForm.personaClipId;
    } else if (genForm.artistClipId) {
      editedClipId = genForm.artistClipId;
    }

    const metric = {
      isMobile: isMobile,
      continueClipId: genForm.continueClipId,
      reusePromptClipId: genForm.reusePromptClipId,
      actionName: actionName,
      clickSourceUrl: location.pathname,
      userId: session.userId,
      isCustomMode: !genForm.isSimple,
      isMadLibs: genForm.isMadLibs,
      isCover: !!genForm.coverClipId,
      isArtistConsistency: !!genForm.artistClipId,
      isPersona: !!genForm.personaClipId,
      editedClipId: editedClipId,
      hasLyrics: (genForm.lyrics || '').length !== 0,
      hasStyle: (genForm.style || '').length !== 0,
      style: genForm.style,
      hasNegativeTags:
        genForm.enableExcludeStyle && genForm.negativeTags.length !== 0,
      negativeTags: genForm.enableExcludeStyle ? genForm.negativeTags : null,
      enableExcludeStyle: genForm.enableExcludeStyle,
      hasTitle: (genForm.title || '').length !== 0,
      hasDescription: (genForm.description || '').length !== 0,
      isInstrumentalMode: genForm.instrumental,
      modelVersion: genForm.mv,
      isUserExtendSongOwner:
        session?.userId !== undefined &&
        session?.userId === genForm.continueClipUserId,
      isUserResusePromptSongOwner:
        session?.userId !== undefined &&
        session?.userId === genForm.reusePromptClipUserId,
      isRecommendStyleUsed: genForm.isRecommendStyleUsed,
      isContinueClipPublic: genForm.continueClipIsPublic,
      isReusePromptClipPublic: genForm.reusePromptClipIsPublic,
      generateInstanceToken: genForm.createSessionToken,
      ...extraFields,
    };
    eventLogger.segmentTrack(EventNames.audioCreationEvent, metric, session);
  };
}

// Export a single instance of the event logger.
export const eventLogger = new EventLogger();
