import {
  findNodeHandle,
  Linking,
  NativeModules,
  NativeSyntheticEvent,
  Platform,
  requireNativeComponent,
  ScaledSize,
  UIManager,
  useWindowDimensions,
} from 'react-native';
import React, { useEffect, useRef } from 'react';
import type { LinkError } from '../types/link-error';
import { useLinkPayContext } from './link-pay-context';
import type { SessionResult } from '../types/session-result';

interface GoBackEvent {
  source: string;
}

const LINKING_ERROR =
  `The package 'linkpay-reactnative' doesn't seem to be linked. Make sure: \n\n` +
  Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
  '- You rebuilt the app after installing the package\n' +
  '- You are not using Expo managed workflow\n';

const ComponentName =
  Platform.OS === 'ios'
    ? 'LinkReactNativeSdkView'
    : 'LinkReactNativeSdkViewManager';

type LinkReactNativeSdkProps = {
  sessionKey: string;
  environment: string;
  redirectUrl: string;
  ref: React.MutableRefObject<null>;
  onSuccess: (event: NativeSyntheticEvent<SessionResult>) => void;
  onFailure: (event: NativeSyntheticEvent<LinkError>) => void;
  onBack: (event: NativeSyntheticEvent<GoBackEvent>) => void;
};

type LinkPayProps = {
  sessionKey: string;
  environment: string;
  redirectUrl: string;
  scaledSize: ScaledSize;
  id: React.MutableRefObject<null>;
  onSuccess: (event: NativeSyntheticEvent<SessionResult>) => void;
  onFailure: (event: NativeSyntheticEvent<LinkError>) => void;
  onBack: (event: NativeSyntheticEvent<GoBackEvent>) => void;
};

const LinkReactNativeSdkViewManager =
  UIManager.getViewManagerConfig(ComponentName) !== null
    ? requireNativeComponent<LinkReactNativeSdkProps>(ComponentName)
    : () => {
        throw new Error(LINKING_ERROR);
      };

const LinkPayViewManager = (props: LinkPayProps) => {
  return (
    <LinkReactNativeSdkViewManager
      sessionKey={props.sessionKey}
      environment={props.environment}
      redirectUrl={props.redirectUrl}
      ref={props.id}
      onSuccess={props.onSuccess}
      onFailure={props.onFailure}
      onBack={props.onBack}
    />
  );
};

const createFragment = (viewId: number) =>
  UIManager.dispatchViewManagerCommand(
    viewId,
    UIManager.getViewManagerConfig(ComponentName).Commands.create!.toString(),
    [viewId]
  );

export function LinkPayView({ navigation }: { navigation: any }) {
  const ref = useRef(null);
  const dimensions = useWindowDimensions();

  useEffect(() => {
    if (Platform.OS === 'android') {
      const viewId = findNodeHandle(ref.current);
      createFragment(viewId!);
    } else if (Platform.OS === 'ios') {
      Linking.removeAllListeners('url');
      Linking.addEventListener('url', (event) =>
        NativeModules.LinkReactNativeSdkViewManager.onOpenURL(event.url)
      );
    }
  }, []);

  useEffect(() => {
    navigation.setOptions({
      headerTitle: '',
      headerShown: false,
    });
  }, [navigation]);

  let { sessionKey, environment, redirectUrl, setSessionResult, setFailure } =
    useLinkPayContext();

  const onSuccess = (event: NativeSyntheticEvent<SessionResult>) => {
    setSessionResult(event.nativeEvent);
    navigation.goBack();
  };

  const onFailure = (event: NativeSyntheticEvent<LinkError>) => {
    setFailure(event.nativeEvent);
    navigation.goBack();
  };

  const onBack = (event: NativeSyntheticEvent<GoBackEvent>) => {
    console.log(`onBack called for source ${event.nativeEvent.source}`);
    navigation.goBack();
  };

  return (
    <LinkPayViewManager
      sessionKey={sessionKey}
      environment={environment}
      redirectUrl={redirectUrl}
      scaledSize={dimensions}
      id={ref}
      onSuccess={onSuccess}
      onFailure={onFailure}
      onBack={onBack}
    />
  );
}
