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

import type {
  AmazonAuthResult,
  AmazonAuthResultSuccess,
} from '~/src/pages/api/amazon/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 { AMAZON_AUTH_PAYLOAD_PARAM } from './constants';

export * from './constants';

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

export type AmazonAuthProps = ServiceAuthProps<AmazonAuthResultSuccess>;

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

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

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

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

        break;
      case 'AMAZON_AUTH_ERROR':
        // https://developer.amazon.com/docs/login-with-amazon/authorization-code-grant.html#authorization-errors
        if (payload.error !== 'access_denied') {
          const message = 'Amazon Auth error';

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

          onError?.(message);
        }

        break;
    }
  };

  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<AmazonAuthResultSuccess>(data);

      const shouldHandle =
        !!payload?.type && payload.type.startsWith('AMAZON_');

      // 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<AmazonAuthResultSuccess>(
      fallbackAuthPayloadParam
    );

    if (!authPayload) {
      return;
    }

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

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

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

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

    // open the popup using the underlying baseUrl as pages can
    // be scoped behind a custom domain and amazon oauth callback
    // needs to match predefined url (ie. songwhip.com/api/amazon/auth/callback)
    childWinRef.current = openPopup({
      url: url.toString(),

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