import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
import { NativeSyntheticEvent, Platform, StyleSheet, View } from 'react-native';
import { BarcodeItem, autorelease, createSBError } from '../../types';
import NativeBarcodeCameraView, {
  Commands,
} from '../spec/ScanbotBarcodeScannerViewNativeComponent';
import {
  ScanbotBarcodeCameraViewHandle,
  ScanbotBarcodeCameraViewProperties,
} from './ScanbotBarcodeCameraViewProperties';

export const ScanbotBarcodeCameraView = forwardRef<
  ScanbotBarcodeCameraViewHandle,
  ScanbotBarcodeCameraViewProperties
>(({ onBarcodeScannerResult, onBarcodeTap, onError, ...props }, forwardedRef) => {
  const viewRef = useRef(null);
  const boundBarcodeItemOverlayViews = useRef<Map<string, number>>(new Map());

  useImperativeHandle(forwardedRef, () => {
    return {
      freezeCamera() {
        if (viewRef.current) {
          Commands.freezeCamera(viewRef.current);
        }
      },

      unfreezeCamera() {
        if (viewRef.current) {
          Commands.unfreezeCamera(viewRef.current);
        }
      },
    };
  }, []);

  const _onBarcodeScannerResult = useCallback(
    async (event: NativeSyntheticEvent<any>) => {
      const result = event.nativeEvent.result;

      if (result) {
        const barcodeItems = (Platform.OS === 'ios' ? JSON.parse(result) : result) as any[];

        autorelease(async () => {
          await onBarcodeScannerResult(barcodeItems.map((item) => new BarcodeItem(item)));
        });

        if (props.selectionOverlayConfig?.barcodeItemOverlayViewBinder) {
          for (let barcode of barcodeItems) {
            const barcodeItemUuid = barcode.text + barcode.upcEanExtension + '_' + barcode.format;

            const barcodeItemNextBind = boundBarcodeItemOverlayViews.current.get(barcodeItemUuid);

            if (
              barcodeItemNextBind === undefined ||
              (barcodeItemNextBind !== -1 && Date.now() >= barcodeItemNextBind)
            ) {
              boundBarcodeItemOverlayViews.current.set(barcodeItemUuid, -1);

              const bindingConfig =
                await props.selectionOverlayConfig.barcodeItemOverlayViewBinder(barcode);

              if (bindingConfig.refreshRate !== undefined && bindingConfig.refreshRate > 0) {
                boundBarcodeItemOverlayViews.current.set(
                  barcodeItemUuid,
                  Date.now() + bindingConfig.refreshRate
                );
              }

              if (viewRef.current != null) {
                Commands.bindBarcodeItemOverlayView(
                  viewRef.current,
                  barcodeItemUuid,
                  JSON.stringify(bindingConfig)
                );
              }
            }
          }
        } else {
          boundBarcodeItemOverlayViews.current.clear();
        }
      }
    },
    [props.selectionOverlayConfig, boundBarcodeItemOverlayViews, onBarcodeScannerResult]
  );

  const _onBarcodeSelectionResult = useCallback(
    (event: NativeSyntheticEvent<any>) => {
      const result = event.nativeEvent.result;
      if (result && onBarcodeTap) {
        autorelease(() => {
          onBarcodeTap(
            new BarcodeItem((Platform.OS === 'ios' ? JSON.parse(result)[0] : result) as any)
          );
        });
      }
    },
    [onBarcodeTap]
  );

  const _onError = useCallback(
    (event: NativeSyntheticEvent<any>) => {
      if (onError) {
        onError(createSBError(event.nativeEvent));
      }
    },
    [onError]
  );

  return (
    <View style={[styles.container, props.style]}>
      <View
        pointerEvents={'box-none'}
        style={[styles.contentContainerStyle, props.contentContainerStyle]}
      >
        {props.children}
      </View>
      <NativeBarcodeCameraView
        //FinderConfigs
        finderEnabled={props.finderConfig?.viewFinderEnabled}
        finderStrokeWidth={props.finderConfig?.finderLineWidth}
        finderMinPadding={props.finderConfig?.minPadding}
        finderStrokeColor={props.finderConfig?.finderLineColor}
        finderOverlayColor={props.finderConfig?.overlayColor}
        finderInset={props.finderConfig?.finderInset}
        finderRequiredAspectRatios={props.finderConfig?.requiredAspectRatio}
        //CameraConfigs
        cameraZoomFactor={Platform.select({
          ios: props.cameraConfig?.cameraZoomFactor
            ? parseFloat(props.cameraConfig?.cameraZoomFactor.toFixed(2))
            : undefined,
          android:
            props.cameraConfig?.cameraZoomFactor !== undefined
              ? (props.cameraConfig?.cameraZoomRange?.minZoom || 1) +
                ((props.cameraConfig?.cameraZoomRange?.maxZoom || 12) -
                  (props.cameraConfig?.cameraZoomRange?.minZoom || 1)) *
                  props.cameraConfig?.cameraZoomFactor
              : undefined,
        })}
        cameraZoomRange={props.cameraConfig?.cameraZoomRange}
        minFocusDistanceLock={props.cameraConfig?.minFocusDistanceLock}
        cameraModule={props.cameraConfig?.cameraModule}
        //SelectionOverlayConfig
        overlayEnabled={props.selectionOverlayConfig?.overlayEnabled}
        overlayPolygonColor={props.selectionOverlayConfig?.polygonColor}
        overlayStrokeColor={props.selectionOverlayConfig?.strokeColor}
        overlayTextFormat={props.selectionOverlayConfig?.textFormat}
        overlayTextColor={props.selectionOverlayConfig?.textColor}
        overlayTextContainerColor={props.selectionOverlayConfig?.textContainerColor}
        overlayLoadingTextValue={props.selectionOverlayConfig?.loadingTextValue}
        overlayBarcodeItemOverlayViewBinder={
          props.selectionOverlayConfig?.barcodeItemOverlayViewBinder !== undefined
        }
        //Barcode Scanner Configuration
        configFormatConfigurations={props.barcodeScannerConfiguration?.barcodeFormatConfigurations}
        configExtractedDocumentFormats={props.barcodeScannerConfiguration?.extractedDocumentFormats}
        configOnlyAcceptDocuments={props.barcodeScannerConfiguration?.onlyAcceptDocuments}
        configReturnBarcodeImage={props.barcodeScannerConfiguration?.returnBarcodeImage}
        configEngineMode={props.barcodeScannerConfiguration?.engineMode}
        configAccumulation={props.barcodeScannerConfiguration?.accumulationConfig}
        configOptimizedForOverlays={props.barcodeScannerConfiguration?.optimizedForOverlays}
        //Other
        flashEnabled={props.flashEnabled}
        scanningEnabled={props.scanningEnabled}
        hardwareButtonsEnabled={props.hardwareButtonsEnabled}
        //View
        style={styles.container}
        ref={viewRef}
        //Result
        onBarcodeScannerResult={_onBarcodeScannerResult}
        onSelectBarcodeResult={_onBarcodeSelectionResult}
        onError={_onError}
      />
    </View>
  );
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  contentContainerStyle: {
    position: 'absolute',
    width: '100%',
    height: '100%',
    zIndex: 2,
  },
});
