import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
import { NativeSyntheticEvent, Platform, StyleSheet, View } from 'react-native';
import {
  DeepPartial,
  DocumentDetectionResult,
  ImageRef,
  autorelease,
  createSBError,
} from '../../types';
import NativeDocumentScannerView, {
  Commands,
  onNativeDocumentDetectionResult,
  onNativeDocumentScannerResult,
  onNativeError,
} from '../spec/ScanbotDocumentScannerViewNativeComponent';
import {
  ScanbotDocumentScannerViewHandle,
  ScanbotDocumentScannerViewProperties,
} from './ScanbotDocumentScannerViewProperties';

export const ScanbotDocumentScannerView = forwardRef<
  ScanbotDocumentScannerViewHandle,
  ScanbotDocumentScannerViewProperties
>(({ onSnappedDocumentResult, onFrameDetectionResult, onError, ...props }, forwardedRef) => {
  const viewRef = useRef(null);

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

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

      snapDocument(acquireFocus?: boolean) {
        if (viewRef.current) {
          Commands.snapDocument(viewRef.current, acquireFocus === true);
        }
      },
    };
  }, []);

  const _onSnappedDocumentResult = useCallback(
    (event: NativeSyntheticEvent<onNativeDocumentScannerResult>) => {
      const originalImageUuid = event.nativeEvent.originalImage;
      if (originalImageUuid) {
        autorelease(async () => {
          try {
            const originalImage = ImageRef.from({
              uniqueId: originalImageUuid,
            } as DeepPartial<ImageRef>);

            const documentImage = event.nativeEvent.documentImage
              ? ImageRef.from({
                  uniqueId: event.nativeEvent.documentImage,
                } as DeepPartial<ImageRef>)
              : undefined;

            const detectionResult = event.nativeEvent.detectionResult
              ? new DocumentDetectionResult(
                  (Platform.OS === 'ios'
                    ? JSON.parse(event.nativeEvent.detectionResult)
                    : event.nativeEvent.detectionResult) as DeepPartial<DocumentDetectionResult>
                )
              : undefined;

            await onSnappedDocumentResult(originalImage, documentImage, detectionResult);
          } catch (e) {
            onError && onError(createSBError(e));
          }
        });
      }
    },
    [onSnappedDocumentResult, onError]
  );

  const _onFrameDetectionResult = useCallback(
    (event: NativeSyntheticEvent<onNativeDocumentDetectionResult>) => {
      if (event.nativeEvent.result) {
        try {
          const detectionResult = new DocumentDetectionResult(
            (Platform.OS === 'ios'
              ? JSON.parse(event.nativeEvent.result)
              : event.nativeEvent.result) as DeepPartial<DocumentDetectionResult>
          );
          onFrameDetectionResult && onFrameDetectionResult(detectionResult);
        } catch (e) {
          onError && onError(createSBError(e));
        }
      }
    },
    [onFrameDetectionResult, onError]
  );

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

  return (
    <View style={[styles.container, props.style]}>
      <NativeDocumentScannerView
        ref={viewRef}
        style={styles.container}
        onDocumentScannerResult={_onSnappedDocumentResult}
        onDetectionResult={_onFrameDetectionResult}
        onError={_onError}
        flashEnabled={props.flashEnabled}
        hardwareButtonsEnabled={props.hardwareButtonsEnabled}
        detectDocumentAfterSnap={props.detectDocumentAfterSnap}
        acceptedAngleScore={props.acceptedAngleScore}
        acceptedBrightnessThreshold={props.acceptedBrightnessThreshold}
        acceptedSizeScore={props.acceptedSizeScore}
        acceptedAspectRatioScore={props.acceptedAspectRatioScore}
        requiredAspectRatios={props.aspectRatios}
        ignoreOrientationMismatch={props.ignoreOrientationMismatch}
        partiallyVisibleDocumentConfiguration={props.partiallyVisibleDocumentConfiguration}
        autoSnappingEnabled={props.autoSnappingEnabled}
        autoSnappingSensitivity={props.autoSnappingSensitivity}
        autoSnappingDelay={props.autoSnappingDelay}
        cameraModule={props.cameraModule}
        cameraPreviewMode={props.cameraPreviewMode}
        photoQualityPrioritization={props.photoQualityPrioritization}
        touchToFocusEnabled={props.touchToFocusEnabled}
        finderEnabled={props.finderEnabled}
        finderLineColor={props.finderLineColor}
        finderLineWidth={props.finderLineWidth}
        finderOverlayColor={props.finderOverlayColor}
        finderCornerRadius={props.finderCornerRadius}
        finderMinimumPadding={props.finderMinimumPadding}
        finderAspectRatio={props.finderAspectRatio}
        polygonEnabled={props.polygonEnabled}
        polygonBackgroundColor={props.polygonBackgroundColor}
        polygonBackgroundColorOK={props.polygonBackgroundColorOK}
        polygonColor={props.polygonColor}
        polygonColorOK={props.polygonColorOK}
        polygonLineWidth={props.polygonLineWidth}
        polygonCornerRadius={props.polygonCornerRadius}
        polygonAutoSnapProgressColor={props.polygonAutoSnapProgressColor}
        polygonAutoSnapProgressLineWidth={props.polygonAutoSnapProgressLineWidth}
        polygonAutoSnapProgressEnabled={props.polygonAutoSnapProgressEnabled}
      />
    </View>
  );
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});
