import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
import { VisionCameraProxy, type Frame } from 'react-native-vision-camera';
import type {
  AndroidBarcode,
  Barcode,
  BarcodeType,
  FrameProcessorPlugin,
  VisionCameraConstants,
  iOSBarcode,
} from './types';
import { normalizeNativeBarcode } from './utils';

const LINKING_ERROR =
  `The package 'vision-camera-code-scanner' 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 Go\n';

export const VisionCameraSimpleScanner = NativeModules.VisionCameraSimpleScanner
  ? NativeModules.VisionCameraSimpleScanner
  : new Proxy(
      {},
      {
        get() {
          throw new Error(LINKING_ERROR);
        },
      },
    );

const { MODULE_NAME, BARCODE_TYPES, BARCODE_FORMATS } =
  VisionCameraSimpleScanner.getConstants() as VisionCameraConstants;

export { BARCODE_FORMATS, BARCODE_TYPES };

const visionCameraEventEmitter = new NativeEventEmitter(
  VisionCameraSimpleScanner,
);

export const onBarcodeDetected = (
  callback: (barcode: iOSBarcode | AndroidBarcode) => unknown,
) => {
  visionCameraEventEmitter.addListener('onBarcodeDetected', (nativeBarcode) => {
    callback(nativeBarcode);
  });
};

const visionCameraProcessorPlugin = VisionCameraProxy.initFrameProcessorPlugin(
  MODULE_NAME,
) as FrameProcessorPlugin | null;

export type ScanBarcodesOptions = {
  barcodeTypes?: BarcodeType[];
  regionOfInterest?: [number, number, number, number];
};

export const scanCodes = (
  frame: Frame,
  options?: ScanBarcodesOptions,
): Barcode[] => {
  'worklet';
  if (visionCameraProcessorPlugin == null) {
    throw new Error(`Failed to load Frame Processor Plugin "${MODULE_NAME}"!`);
  }

  // console.log(`frame.width=${frame.width}, frame.height=${frame.height}`);
  // @ts-expect-error - incrementRefCount() is not exposed in the typings
  frame.incrementRefCount && frame.incrementRefCount();
  const nativeCodes = visionCameraProcessorPlugin.call(
    frame,
    options,
  ) as unknown as (AndroidBarcode | iOSBarcode)[];
  // @ts-expect-error - decrementRefCount() is not exposed in the typings
  frame.decrementRefCount && frame.decrementRefCount();

  if (!Array.isArray(nativeCodes)) {
    console.log(
      `Native frame processor ${MODULE_NAME} failed to return a proper array!`,
      nativeCodes,
    );
    return [];
  }
  return nativeCodes
    .slice()
    .map((nativeBarcode) => normalizeNativeBarcode(nativeBarcode, frame));
};
