'use client';

import { useAuth, useUser } from '@clerk/nextjs';
import type { UserResource } from '@clerk/types';
import { parseISO } from 'date-fns';
import {
  ReadonlyURLSearchParams,
  usePathname,
  useSearchParams,
} from 'next/navigation';
import { useEffect, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { DEFAULT_CURRENCY } from '@/app/(root)/account/constants';
import { ApiClient } from '@/lib/apiClient';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { SessionStore } from '@/state/sessionStore';
import {
  MILLISECONDS_PER_DAY,
  REDIRECTED_FROM_PARAM,
  REDIRECTED_FROM_VALUES,
  REFERRER_PARAM,
  SIGNUP_SOURCE_PARAM,
  SIGNUP_SOURCE_VALUES,
} from '@/utils/constants';

import { AXON_ITEM_CATEGORY_ID } from './constants';
import { createAxonItemVariantIdForSubscription, useAxon } from './useAxon';
import { useSingular } from './useSingular';
import {
  getLSShareData,
  getTrackingObject,
  isNewAction,
  logShareAttribution,
  pushGtmUserEvent,
  setLSShareData,
  setTrackingObject,
} from './utils';

// Helps us avoid redundantly logging events if user repeatedly visits
// a page with search params that trigger a new event.
const TRACKING_DATA_TTL_DAYS = 30;

type AFEventProps = {
  eventType: 'EVENT';
  eventName: string;
  eventRevenue?: number;
  eventCurrency?: string;
  eventValue?: Record<string, any>;
};

declare global {
  interface Window {
    AF: (
      sdkType: 'pba',
      dataType: string,
      params?: string | AFEventProps
    ) => void;
    dataLayer: any[];
  }
}

/**
 * Tracks key user events in Google Tag Manager (GTM).
 */
export default function Ga4AnalyticsTracker({
  session,
}: {
  session: SessionStore;
}) {
  const { isLoaded, isSignedIn } = useAuth();
  const { user: clerkUser } = useUser();
  const { ga4 } = useStores();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const { publishSignupEvent, publishSubscriptionEvent } = useSingular(session);
  const {
    publishSignupEvent: publishAxonSignupEvent,
    publishSubscriptionEvent: publishAxonSubscriptionEvent,
    publishPageViewEvent: publishAxonPageViewEvent,
    publishPurchaseEvent: publishAxonPurchaseEvent,
  } = useAxon();

  useEffect(() => {
    setLSShareData(searchParams.get('sh'), null, null);
  }, [searchParams]);

  useEffect(() => {
    publishAxonPageViewEvent();
  }, [pathname, publishAxonPageViewEvent]);

  useEffect(() => {
    if (!isLoaded || !isSignedIn || !clerkUser) return;

    handleSignUp({
      clerkUser,
      searchParams,
      session,
      apiClient: ga4.apiClient,
      pathname,
      publishSignupEvent,
      publishAxonSignupEvent,
    });
    handleSubscription({
      clerkUser,
      searchParams,
      session,
      publishSubscriptionEvent,
      publishAxonSubscriptionEvent,
      publishAxonPurchaseEvent,
    });
  }, [
    isLoaded,
    isSignedIn,
    clerkUser,
    pathname,
    searchParams,
    session,
    session.sessionIsLoaded,
    session.isSubLoaded,
    ga4,
    publishSignupEvent,
    publishSubscriptionEvent,
    publishAxonSignupEvent,
    publishAxonSubscriptionEvent,
    publishAxonPurchaseEvent,
  ]);

  return null;
}

type HandleSubscriptionParams = {
  clerkUser: UserResource;
  searchParams: ReadonlyURLSearchParams;
  session: SessionStore;
  publishSubscriptionEvent: (subData: any, eventId: string) => void;
  publishAxonSubscriptionEvent: (subscriptionData: {
    currency?: string;
    value?: number;
    user_id?: string;
  }) => void;
  publishAxonPurchaseEvent: (purchaseData: {
    currency: string;
    value: number;
    items: any[];
    transaction_id: string;
    user_id?: string;
    shipping?: number;
    tax?: number;
  }) => void;
};

const handleSubscription = ({
  clerkUser,
  searchParams,
  session,
  publishSubscriptionEvent,
  publishAxonSubscriptionEvent,
  publishAxonPurchaseEvent,
}: HandleSubscriptionParams) => {
  if (!session.sessionIsLoaded || !session.isSubLoaded) {
    return;
  }

  const getSubscriptionMetadata = (sub: Record<string, any>) => {
    return {
      subscription_id: sub.subscription_id,
      value: sub.plan_price,
      currency: DEFAULT_CURRENCY,
      items: [
        {
          item_id: sub.plan.id,
          item_name: sub.plan.name,
          item_category: sub.period,
          price: sub.plan_price,
          quantity: 1,
        },
      ],
    };
  };

  const getAxonSubscriptionMetadata = (sub: Record<string, any>) => {
    return {
      currency: DEFAULT_CURRENCY,
      value: sub.plan_price,
      items: [
        {
          item_variant_id: createAxonItemVariantIdForSubscription(
            sub.plan.plan_key,
            sub.period
          ),
          item_id: sub.plan.plan_key,
          item_name: sub.plan.name,
          price: sub.plan_price,
          quantity: 1,
          item_category_id: AXON_ITEM_CATEGORY_ID,
        },
      ],
      transaction_id: sub.subscription_id,
      user_id: session.userId || undefined,
    };
  };

  if (
    !searchParams.get('checkout_session_id') ||
    !session.sub?.subscription_anchor
  ) {
    return;
  }

  // Use localStorage to track if we've already sent new user signup event
  const trackingKey = `user-subscription-${clerkUser.id}`;
  const trackedObject = getTrackingObject(trackingKey);

  const isNewSubscription = isNewAction(
    parseISO(session.sub?.subscription_anchor)
  );

  if (
    isNewSubscription &&
    (!trackedObject.expires || Date.now() > trackedObject.expires)
  ) {
    session.loadSubscriptionInfo();
    const subMetadata = getSubscriptionMetadata(session.sub);
    const axonSubMetadata = getAxonSubscriptionMetadata(session.sub);

    // Event ID is for Meta Conversions API
    const eventId = crypto.randomUUID();
    pushGtmUserEvent(clerkUser, 'user_subscription', subMetadata, eventId);

    // Applovin subscribe event
    publishAxonSubscriptionEvent({
      currency: axonSubMetadata.currency,
      value: axonSubMetadata.value,
      user_id: axonSubMetadata.user_id,
    });

    // Applovin purchase event
    publishAxonPurchaseEvent(axonSubMetadata);

    // Publish to Singular
    publishSubscriptionEvent(subMetadata, eventId);

    // Set new expiry
    const expiryTime =
      Date.now() + TRACKING_DATA_TTL_DAYS * MILLISECONDS_PER_DAY;
    setTrackingObject(trackingKey, expiryTime);
  }
};

type HandleSignUpParams = {
  clerkUser: UserResource;
  searchParams: ReadonlyURLSearchParams;
  session: SessionStore;
  apiClient: ApiClient;
  pathname: string;
  publishSignupEvent: (eventId: string) => void;
  publishAxonSignupEvent: (userId?: string) => void;
};

const handleSignUp = ({
  clerkUser,
  searchParams,
  session,
  apiClient,
  pathname,
  publishSignupEvent,
  publishAxonSignupEvent,
}: HandleSignUpParams) => {
  if (!clerkUser.createdAt || !session.sessionIsLoaded) {
    return;
  }

  const redirectedFrom = searchParams.get(REDIRECTED_FROM_PARAM);
  const signupSource = searchParams.get(SIGNUP_SOURCE_PARAM);
  const referrer = searchParams.get(REFERRER_PARAM);
  const isNewClerkUser = isNewAction(clerkUser.createdAt);

  // Adds new attribution event for signup and signin
  if (signupSource && signupSource !== undefined) {
    if (isNewClerkUser) {
      logWebUserEvent({
        actionName: 'UserSignedUpFromPage',
        context: {
          signupSource: signupSource,
          referrer: referrer || undefined,
          currentPathname: pathname,
        },
      });
    } else {
      logWebUserEvent({
        actionName: 'UserSignedInFromPage',
        context: {
          signinSource: signupSource,
          referrer: referrer || undefined,
          currentPathname: pathname,
        },
      });
    }
  }

  // [START TODO] Remove this once we conclude the mobile song page v2 experiment
  // Use UserSignedUpFromPage and UserSignedInFromPage events instead
  if (
    signupSource &&
    (signupSource === SIGNUP_SOURCE_VALUES.MOBILE_SONG_PAGE ||
      signupSource === SIGNUP_SOURCE_VALUES.SONG_PAGE)
  ) {
    if (isNewClerkUser) {
      logWebUserEvent({
        actionName: 'SignedUpOnSongPage',
        context: {
          signupSource: signupSource,
          referrer: referrer || undefined,
        },
      });
    } else {
      logWebUserEvent({
        actionName: 'SignedInOnSongPage',
        context: {
          signinSource: signupSource,
          referrer: referrer || undefined,
        },
      });
    }
  }
  // [END TODO]

  if (!redirectedFrom) {
    return;
  }

  // Use localStorage to track if we've already sent new user signup event
  const trackingKey = `user-auth-${clerkUser.id}`;
  const trackedObject = getTrackingObject(trackingKey);

  const isNewUser =
    isNewAction(clerkUser.createdAt) &&
    redirectedFrom === REDIRECTED_FROM_VALUES.SIGNUP;

  // Track if we haven't tracked before or if tracking has expired
  if (!trackedObject.expires || Date.now() > trackedObject.expires) {
    // Event ID is for Meta Conversions API
    const eventId = crypto.randomUUID();
    if (isNewUser) {
      pushGtmUserEvent(
        clerkUser,
        'user_signup',
        { user_status: 'new_user' },
        eventId
      );

      // Applovin sign_up event
      publishAxonSignupEvent(session.userId || undefined);

      // Publish to Singular
      publishSignupEvent(eventId);

      // Record backend event for share attribution
      // get the share content from localstorage
      const shareData = getLSShareData();
      const hasShareId = shareData && shareData.sid;
      const hasContentId = shareData && shareData.scid && shareData.sct;
      if (hasShareId || hasContentId) {
        logShareAttribution(apiClient, shareData);
      }
    } else {
      pushGtmUserEvent(
        clerkUser,
        'user_signin',
        {
          user_status: 'existing_user',
        },
        eventId
      );
    }

    // Set new expiry
    const expiryTime =
      Date.now() + TRACKING_DATA_TTL_DAYS * MILLISECONDS_PER_DAY;
    setTrackingObject(trackingKey, expiryTime);
  }
};

export const useGa4InitialCreateEvent = () => {
  const [triedCreate, setTriedCreate] = useState(false);
  const { ga4 } = useStores();
  const { user: clerkUser } = useUser();
  const { isSignedIn } = useAuth();

  useEffect(() => {
    const checkAndLogCreation = async () => {
      if (!triedCreate || !isSignedIn || !clerkUser) {
        return;
      }

      if (clerkUser) {
        await ga4.loadHasCreatedSong();
        const hasUserCreatedSongBefore = ga4.hasCreatedSong;
        if (!hasUserCreatedSongBefore) {
          // Event ID is for Meta Conversions API
          const eventId = crypto.randomUUID();
          pushGtmUserEvent(
            clerkUser,
            'user_initial_song_creation',
            null,
            eventId
          );
        }
      }
    };

    checkAndLogCreation();
  }, [triedCreate, clerkUser, isSignedIn, ga4]);

  return { triedCreate, setTriedCreate };
};
