import { useCallback, useEffect, useRef } from 'react';
import Debug from 'debug';

import type {
  DeezerAuthResult,
  DeezerAuthResultSuccess,
} from '~/src/pages/api/deezer/auth/callback';
import type { ServiceAuthProps, ServiceAuthStartParams } from '../types';

import { tryParseJson } from '~/lib/utils/object';
import getPublicEndpoint from '~/src/lib/getPublicEndpoint';
import { useAppRouter } from '~/src/lib/router2';
import { reportErrorClient } from '~/src/lib/sentry/client';
import openPopup from '~/src/lib/utils/openPopup';
import { DEEZER_AUTH_RESULT_PARAM } from './constants';

const debug = Debug('songwhip/useDeezerAuth');

export type DeezerAuthProps = ServiceAuthProps<DeezerAuthResultSuccess>;

export const useDeezerAuth = ({ onSuccess, onError }: DeezerAuthProps) => {
  const childWinRef = useRef<Window | null>(null);
  const appRouter = useAppRouter();

  const fallbackAuthPayloadParam =
    appRouter.getAsQuery()[DEEZER_AUTH_RESULT_PARAM];

  const handleAuthPayload = (payload: DeezerAuthResult) => {
    debug('handle auth payload', payload);

    switch (payload.type) {
      case 'DEEZER_AUTH_SUCCESS':
        onSuccess(payload);

        break;
      case 'DEEZER_AUTH_ERROR': {
        const errorMessage = 'Deezer Auth error';

        reportErrorClient({
          error: new Error(errorMessage),
          extras: { error: payload.error },
        });

        onError?.(errorMessage);

        break;
      }
    }
  };

  /**
   * Most of the time the Deezer auth popup will postMessage the payload
   * back to this window, this is where we listen and handle post messages.
   */
  useEffect(() => {
    // the child popup window will postMessage this window when
    // the auth flow is complete, passing the google token payload
    const onMessage = ({ data }) => {
      debug('postMessage', data);
      const payload = tryParseJson<DeezerAuthResult>(data);
      const shouldHandle = payload?.type && !!~payload.type.indexOf('DEEZER_');

      // abort if payload isn't as expected
      if (!shouldHandle) return;

      // close that popup asap
      childWinRef.current?.close();

      handleAuthPayload(payload);
    };

    addEventListener('message', onMessage);

    // detach global event listener on unmount
    return () => {
      removeEventListener('message', onMessage);
    };
  }, []);

  /**
   * When oauth popup flow fails to postMessage back to the parent window
   * it will redirect back to the `fallbackPath` passing the auth payload
   * as a query param. This can happen when app is running inside a webview.
   */
  useEffect(() => {
    if (!fallbackAuthPayloadParam) return;

    const authPayload = tryParseJson<DeezerAuthResult>(
      fallbackAuthPayloadParam
    );

    if (!authPayload) {
      return;
    }

    // Remove auth result param from URL
    const parsedUrl = new URL(location.href);
    parsedUrl.searchParams.delete(DEEZER_AUTH_RESULT_PARAM);
    history.replaceState(history.state, '', parsedUrl.toString());

    handleAuthPayload(authPayload);
  }, [fallbackAuthPayloadParam]);

  return useCallback(({ user }: ServiceAuthStartParams = {}) => {
    const url = new URL('/api/deezer/auth', getPublicEndpoint());

    url.searchParams.set('pageUrl', location.href);
    if (user) url.searchParams.set('userData', JSON.stringify(user));

    childWinRef.current = openPopup({
      url: url.toString(),

      width: 480,
      height: 640,
    });
  }, []);
};
