import React from 'react';
import type { ReactElement } from 'react';
import type { ViewProps } from 'react-native';
import {
  DecodeHintType,
  BarcodeFormat,
  BrowserMultiFormatReader,
  Result,
} from '@zxing/library';
import styled from '@emotion/styled';
import { Camera, CameraType } from './Camera';
import './styles.css';

interface BarCodeAcceptedFormat {
  acceptedFormats: string[];
}

interface Props extends ViewProps {
  autoFocusEnabled: boolean;
  flashModeEnabled: boolean;
  acceptFormats?: BarCodeAcceptedFormat;
  onScanResultListener?: (event: any) => void;
  onErrorListener?: (event: any) => void;
  frameWidth: number;
  frameHeight: number;
  frameBorderColor?: string;
  screenWidth: number;
  screenHeight: number;
  isTouchSupported?: boolean;
  touchSupportedCallback?: (touchSupported: boolean) => void;
}

export type BarcodeScannerViewType = React.ForwardRefExoticComponent<
  Props & React.RefAttributes<unknown>
> & {
  takePhoto(): string;
  getNumberOfCameras(): number;
  flashStatus(): boolean;
};

const delay = 350;

const BarcodeScannerView = React.forwardRef(
  (props: Props, ref): ReactElement => {
    const {
      frameWidth,
      frameHeight,
      acceptFormats,
      screenHeight,
      flashModeEnabled,
      onScanResultListener,
      onErrorListener,
      isTouchSupported,
      touchSupportedCallback,
    } = props;

    const codeReader = React.useMemo(() => {
      const hints = new Map();
      hints.set(
        DecodeHintType.POSSIBLE_FORMATS,
        mapToSupportedFormats(acceptFormats?.acceptedFormats || [])
      );
      return new BrowserMultiFormatReader(hints);
    }, [acceptFormats?.acceptedFormats]);

    const camera = React.useRef<CameraType>(null);

    React.useImperativeHandle(ref, () => ({
      takePhoto: () => {
        camera?.current?.takePhoto();
      },
      getNumberOfCameras: () => {
        return camera?.current?.getNumberOfCameras() ?? 0;
      },
      flashStatus: () => {
        return camera?.current?.flashStatus() ?? false;
      },
    }));

    const capture = React.useCallback(() => {
      // const imageSrc = webcamRef?.current?.getScreenshot();
      const numberOfCameras = camera?.current?.getNumberOfCameras() ?? 0;

      if (numberOfCameras > 0) {
        const imageSrc = camera?.current?.takePhoto();

        if (imageSrc) {
          codeReader
            .decodeFromImage(undefined, imageSrc)
            .then((result: Result) => {
              const event = {
                nativeEvent: {
                  value: refactorResult(result),
                },
              };
              onScanResultListener?.(event);
            })
            .catch(() => {
              // console.log(err);
              // onErrorListener?.(err);
            });
        }
      }
    }, [codeReader, onScanResultListener]);

    // React.useEffect(() => {
    //   // Turn on the flashlight if prop is defined and device has the capability
    //   if (
    //     typeof torch === 'boolean' &&
    //     // @ts-ignore
    //     navigator?.mediaDevices?.getSupportedConstraints().torch
    //   ) {
    //     const stream = webcamRef?.current?.video?.srcObject as MediaStream;

    //     const track = stream?.getVideoTracks()[0] as MediaStreamTrack; // get the active track of the stream

    //     if (
    //       track &&
    //       track.getCapabilities().torch &&
    //       !track.getConstraints().torch
    //     ) {
    //       track
    //         .applyConstraints({
    //           advanced: [{ torch }],
    //         })
    //         .catch((err: any) => onUpdate(err));
    //     }
    //   }
    // }, [onUpdate]);

    React.useEffect(() => {
      const interval = setInterval(capture, delay);
      return () => {
        clearInterval(interval);
      };
    }, [capture]);

    React.useEffect(() => {
      return () => {
        codeReader.reset();
      };
    }, [codeReader]);

    const FocusView = styled.div<{ frameWidth: string; frameHeight: string }>`
      position: absolute;
      width: ${(props) => props.frameWidth};
      height: ${(props) => props.frameHeight};
      border-radius: 10px;
      box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.5);
    `;
    const Border = styled.div<{ frameWidth: string; frameHeight: string }>`
      --b: 4px; /* thickness of the border */
      --c: #ffb800; /* color of the border */
      --w: 100px; /* width of border */
      --r: 10px; /* radius */

      position: absolute;
      z-index: 5;
      border-color: transparent;
      width: ${(props) => props.frameWidth};
      height: ${(props) => props.frameHeight};
    `;

    const LabelView = styled.div<{ top: string }>`
      background-color: #000000;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      // top: ${(props) => props.top};
      border-radius: 23px;
      padding: 12px 16px;
      border: 1px solid #585858;
      // min-height: 44px;
      background: rgba(0, 0, 0, 0.3);
      backdrop-filter: blur(6.5px);
      webkit-backdrop-filter: blur(6.5px);
    `;
    const LabelText = styled.div`
      font-size: 16px;
      font-weight: 500;
      color: white;
      text-align: center;
      margin: 0;
    `;

    const Container = styled.div<{ top: string }>`
      position: absolute;
      z-index: 5;
      align-items: center;
      justify-content: center;
      width: 100%;
      display: flex;
      top: ${(props) => props.top};
    `;
    const FocusContainer = styled.div`
      // position: absolute;
      // z-index: 5;
      align-items: center;
      justify-content: center;
      width: 100%;
      display: flex;
      flex: 1;
    `;

    return (
      <FocusContainer className="focus-container">
        <Camera
          ref={camera}
          aspectRatio="cover"
          errorMessages={{
            noCameraAccessible: 'NoCameraAccessible',
            permissionDenied: 'PermissionDenied',
            switchCamera: 'NotPossibleToSwitchCamera',
            canvas: 'CanvasIsNotSupported.',
          }}
          flashMode={flashModeEnabled}
          facingMode={'environment'}
          onError={onErrorListener}
          isTouchSupported={isTouchSupported}
          touchSupportedCallback={touchSupportedCallback}
        />
        <Container top={`${(screenHeight - frameHeight) / 2 - 64}px`}>
          <LabelView top={`${(screenHeight - frameHeight) / 2 - 64}px`}>
            <LabelText>Center barcode in scanner.</LabelText>
          </LabelView>
        </Container>

        <FocusView
          frameWidth={`${frameWidth}px`}
          frameHeight={`${frameHeight}px`}
        />
        <Border
          className="border-focus"
          frameWidth={`${frameWidth}px`}
          frameHeight={`${frameHeight}px`}
        />
      </FocusContainer>
    );
  }
);

BarcodeScannerView.displayName = 'BarcodeScannerView';

export default BarcodeScannerView;

const mapToSupportedFormats = (acceptFormats: string[]): BarcodeFormat[] => {
  const supportedFormats: BarcodeFormat[] = [];
  acceptFormats.forEach((format) => {
    switch (format) {
      case 'AZTEC':
        supportedFormats.push(BarcodeFormat.AZTEC);
        break;
      case 'CODABAR':
        supportedFormats.push(BarcodeFormat.CODABAR);
        break;
      case 'CODE_39':
        supportedFormats.push(BarcodeFormat.CODE_39);
        break;
      case 'CODE_93':
        supportedFormats.push(BarcodeFormat.CODE_93);
        break;
      case 'CODE_128':
        supportedFormats.push(BarcodeFormat.CODE_128);
        break;
      case 'EAN_8':
        supportedFormats.push(BarcodeFormat.EAN_8);
        break;
      case 'EAN_13':
        supportedFormats.push(BarcodeFormat.EAN_13);
        break;
      case 'PDF':
        supportedFormats.push(BarcodeFormat.PDF_417);
        break;
      case 'QR_CODE':
        supportedFormats.push(BarcodeFormat.QR_CODE);
        break;
      case 'UPC_A':
        supportedFormats.push(BarcodeFormat.UPC_A);
        break;
      case 'UPC_E':
        supportedFormats.push(BarcodeFormat.UPC_E);
        break;
      default:
        break;
    }
  });

  return supportedFormats;
};

const refactorResult = (result: Result): string => {
  const text = result.getText();
  // const format = result.getBarcodeFormat();
  // const rawBytes = result.getRawBytes();
  // console.log({ text, length: text.length, format, rawBytes });

  // if (text.length === 13 && format === 7) {
  //   console.log('slide');

  //   return text.slice(1);
  // }

  return text;
};
