import type { UserResource } from '@clerk/types';
import storageAvailable from 'storage-available';

import { ApiClient } from '@/lib/apiClient';
import { ShareContentType } from '@/utils/share';

// Number of milliseconds after a user performs a trackable action
// beyond which we do not consider the action new.
const USER_ACTION_TIMEOUT_MS = 120_000;
const SHARE_PARAM_EXPIRY_MS = 30 * 24 * 60 * 60 * 1000;
export const SHARE_PARAM_KEY = 'sh-params';

/**
 * Push user events to Google Tag Manager (GTM).
 * @param eventType - The type of event (e.g. 'user_signup', 'user_signin')
 * @param metadata - Additional metadata to include in the event
 */
export const pushGtmUserEvent = (
  user: UserResource,
  eventType:
    | 'user_signup'
    | 'user_signin'
    | 'user_subscription'
    | 'user_initial_song_creation',
  metadata: Record<string, any> | null = null,
  eventId: string
) => {
  if (typeof window !== 'undefined') {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      event: eventType,
      user_email: user.primaryEmailAddress?.emailAddress ?? '',
      user_phone: user.primaryPhoneNumber?.phoneNumber ?? '',
      event_id: eventId,
      ...metadata,
    });
  }
};

export const getTrackingObject = (trackingKey: string) => {
  let trackedObject: { expires?: number } = {};
  try {
    if (storageAvailable('localStorage')) {
      const trackedValue = localStorage.getItem(trackingKey);
      if (trackedValue) {
        trackedObject = JSON.parse(trackedValue);
      }
    }
  } catch (e) {
    // Invalid JSON, treat as no tracking data
  }
  return trackedObject;
};

export const setTrackingObject = (trackingKey: string, expires: number) => {
  if (storageAvailable('localStorage')) {
    localStorage.setItem(trackingKey, JSON.stringify({ expires }));
  }
};

export const isNewAction = (timestamp: Date) => {
  const now = Date.now();
  const timeDiff = now - timestamp.getTime();
  return timeDiff < USER_ACTION_TIMEOUT_MS;
};

/**
 * Generic utility for storing attribution IDs in localStorage.
 * Used for tracking user acquisition through various sources.
 *
 * When an ID is found in the URL, it's saved with an expiration
 * timestamp for later attribution.
 */
interface ShareAttributionData {
  sid: string | null;
  scid: string | null;
  sct: string | null;
  expiry: number;
}

export const setLSShareData = (
  shareCode: string | null,
  contentType: ShareContentType | null,
  contentId: string | null
) => {
  if (storageAvailable('localStorage')) {
    // First check if we already have attribution data stored
    const storedData = localStorage.getItem(SHARE_PARAM_KEY);
    let existingData = null;
    if (storedData) {
      try {
        existingData = JSON.parse(storedData) as ShareAttributionData;
        // If the data has expired, treat as if we don't have it
        if (existingData.expiry && Date.now() > existingData.expiry) {
          existingData = null;
        }
      } catch (e) {
        // Invalid JSON, treat as no data
        existingData = null;
      }
    }
    existingData = existingData || {
      sid: null,
      scid: null,
      sct: null,
      expiry: null,
    };

    // Check if we have a share ID in the URL params
    if (shareCode) {
      const data = {
        ...existingData,
        sid: shareCode,
        expiry: Date.now() + SHARE_PARAM_EXPIRY_MS,
      };
      localStorage.setItem(SHARE_PARAM_KEY, JSON.stringify(data));
    } else if (contentType && contentId) {
      const data = {
        ...existingData,
        scid: contentId,
        sct: contentType,
        expiry: Date.now() + SHARE_PARAM_EXPIRY_MS,
      };
      localStorage.setItem(SHARE_PARAM_KEY, JSON.stringify(data));
    }
  }
};

export const getLSShareData = (): ShareAttributionData | null => {
  if (!storageAvailable('localStorage')) {
    return null;
  }

  const storedData = localStorage.getItem(SHARE_PARAM_KEY);
  if (!storedData) {
    return null;
  }

  try {
    const data = JSON.parse(storedData) as ShareAttributionData;
    // If the ID has expired, don't return it
    if (!data.expiry || Date.now() > data.expiry) {
      return null;
    }
    return data;
  } catch (e) {
    return null;
  }
};

export const logShareAttribution = async (
  apiClient: ApiClient,
  shareData: ShareAttributionData | null
) => {
  if (!shareData) {
    return;
  }

  await apiClient.POST('/api/share/attribute/', {
    body: {
      source: 'web',
      share_id: shareData.sid ?? '',
      content_id: shareData.scid ?? '',
      content_type: shareData.sct ?? '',
    },
  });
};
