import { createElement, forwardRef, useRef, useCallback, useContext, useState, useEffect, useMemo } from 'react';
import { getCurrentPage, useTransformStyle, useLayout, extendObject } from './utils';
import useInnerProps, { getCustomEvent } from './getInnerListeners';
import { Camera, useCameraDevice, useCodeScanner, useCameraFormat } from 'react-native-vision-camera';
import { noop, warn, hasOwn } from '@mpxjs/utils';
import { RouteContext } from './context';
import { watch } from '@mpxjs/core';
const qualityValue = {
    high: 90,
    normal: 75,
    low: 50,
    original: 100
};
let RecordRes = null;
const _camera = forwardRef((props, ref) => {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const cameraRef = useRef(null);
    const { mode = 'normal', resolution = 'medium', 'device-position': devicePosition = 'back', flash = 'auto', 'frame-size': frameSize = 'medium', bindinitdone, bindstop, bindscancode, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'enable-var': enableVar, 'external-var-context': externalVarContext, style = {} } = props;
    const styleObj = extendObject({}, style);
    const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(styleObj, {
        enableVar,
        externalVarContext,
        parentFontSize,
        parentWidth,
        parentHeight
    });
    const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: cameraRef });
    const isPhoto = useRef(false);
    isPhoto.current = mode === 'normal';
    const device = useCameraDevice(devicePosition || 'back');
    const { navigation, pageId } = useContext(RouteContext) || {};
    const [zoomValue, setZoomValue] = useState(1);
    const [isActive, setIsActive] = useState(true);
    const [hasPermission, setHasPermission] = useState(null);
    const page = getCurrentPage(pageId);
    // 先定义常量，避免在条件判断后使用
    const maxZoom = device?.maxZoom || 1;
    const RESOLUTION_MAPPING = {
        low: { width: 1280, height: 720 },
        medium: { width: 1920, height: 1080 },
        high: 'max'
    };
    const FRAME_SIZE_MAPPING = {
        small: { width: 1280, height: 720 },
        medium: { width: 1920, height: 1080 },
        large: 'max'
    };
    const format = useCameraFormat(device, [
        {
            photoResolution: RESOLUTION_MAPPING[resolution],
            videoResolution: FRAME_SIZE_MAPPING[frameSize] || RESOLUTION_MAPPING[resolution]
        }
    ]);
    const isScancode = useCallback((fail, complete) => {
        if (!isPhoto.current) {
            const result = {
                errMsg: 'Not allow to invoke takePhoto in \'scanCode\' mode.'
            };
            fail(result);
            complete(result);
            return true;
        }
        return false;
    }, []);
    const codeScanner = useCodeScanner({
        codeTypes: ['qr'],
        onCodeScanned: (codes) => {
            codes.forEach(code => {
                const type = code.type === 'qr' ? 'QR_CODE' : code.type?.toUpperCase();
                const frame = code.frame || {};
                bindscancode && bindscancode(getCustomEvent('scancode', {}, {
                    detail: {
                        result: code.value,
                        type,
                        scanArea: [parseInt(frame.x) || 0, parseInt(frame.y) || 0, parseInt(frame.width) || 0, parseInt(frame.height) || 0]
                    }
                }));
            });
        }
    });
    const onInitialized = useCallback(() => {
        bindinitdone && bindinitdone(getCustomEvent('initdone', {}, {
            detail: {
                maxZoom
            }
        }));
    }, [bindinitdone, maxZoom]);
    const onStopped = useCallback(() => {
        bindstop && bindstop();
    }, [bindstop]);
    const camera = useMemo(() => ({
        setZoom: (zoom) => {
            setZoomValue(zoom);
        },
        takePhoto: (options = {}) => {
            const { success = noop, fail = noop, complete = noop } = options;
            if (isScancode(fail, complete))
                return;
            cameraRef.current?.takePhoto?.({
                quality: qualityValue[options.quality || 'normal']
            }).then((res) => {
                const result = {
                    errMsg: 'takePhoto:ok',
                    tempImagePath: res.path
                };
                success(result);
                complete(result);
            }).catch(() => {
                const result = {
                    errMsg: 'takePhoto:fail'
                };
                fail(result);
                complete(result);
            });
        },
        startRecord: (options = {}) => {
            let { timeout = 30, success = noop, fail = noop, complete = noop, timeoutCallback = noop } = options;
            timeout = timeout > 300 ? 300 : timeout;
            let recordTimer = null;
            if (isScancode(fail, complete))
                return;
            try {
                const result = {
                    errMsg: 'startRecord:ok'
                };
                success(result);
                complete(result);
                cameraRef.current?.startRecording?.({
                    onRecordingError: (error) => {
                        if (recordTimer)
                            clearTimeout(recordTimer);
                        const errorResult = {
                            errMsg: 'startRecord:fail during recording',
                            error: error
                        };
                        timeoutCallback(errorResult);
                    },
                    onRecordingFinished: (video) => {
                        RecordRes = video;
                        if (recordTimer)
                            clearTimeout(recordTimer);
                    }
                });
                recordTimer = setTimeout(() => {
                    cameraRef.current?.stopRecording().catch(() => {
                        // 忽略停止录制时的错误
                    });
                }, timeout * 1000);
            }
            catch (error) {
                if (recordTimer)
                    clearTimeout(recordTimer);
                const result = {
                    errMsg: 'startRecord:fail ' + (error.message || 'unknown error')
                };
                fail(result);
                complete(result);
            }
        },
        stopRecord: (options = {}) => {
            const { success = noop, fail = noop, complete = noop } = options;
            if (isScancode(fail, complete))
                return;
            try {
                cameraRef.current?.stopRecording().then(() => {
                    setTimeout(() => {
                        if (RecordRes) {
                            const result = {
                                errMsg: 'stopRecord:ok',
                                tempVideoPath: RecordRes?.path,
                                duration: RecordRes.duration * 1000 // 转成ms
                            };
                            RecordRes = null;
                            success(result);
                            complete(result);
                        }
                    }, 200); // 延时200ms，确保录制结果已准备好
                }).catch((e) => {
                    const result = {
                        errMsg: 'stopRecord:fail ' + (e.message || 'promise rejected')
                    };
                    fail(result);
                    complete(result);
                });
            }
            catch (error) {
                const result = {
                    errMsg: 'stopRecord:fail ' + (error.message || 'unknown error')
                };
                fail(result);
                complete(result);
            }
        }
    }), []);
    useEffect(() => {
        let unWatch;
        if (pageId && hasOwn(global.__mpxPageStatusMap, String(pageId))) {
            unWatch = watch(() => global.__mpxPageStatusMap[pageId], (newVal) => {
                if (newVal === 'show') {
                    if (page.id === pageId) {
                        setIsActive(true);
                    }
                }
                if (newVal === 'hide') {
                    setIsActive(false);
                }
            }, { sync: true });
        }
        const checkCameraPermission = async () => {
            try {
                const cameraPermission = global?.__mpx?.config?.rnConfig?.cameraPermission;
                if (typeof cameraPermission === 'function') {
                    const permissionResult = await cameraPermission();
                    setHasPermission(permissionResult === true);
                }
                else {
                    setHasPermission(true);
                }
            }
            catch (error) {
                setHasPermission(false);
            }
        };
        checkCameraPermission();
        return () => {
            if (navigation?.camera === camera) {
                delete navigation.camera;
            }
            unWatch && unWatch();
        };
    }, []);
    const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
        ref: cameraRef,
        style: extendObject({}, normalStyle, layoutStyle),
        isActive,
        photo: true,
        video: true,
        onInitialized,
        onStopped,
        device,
        format,
        codeScanner: !isPhoto.current ? codeScanner : undefined,
        zoom: zoomValue,
        torch: flash
    }), [
        'mode',
        'resolution',
        'frame-size',
        'bindinitdone',
        'bindstop',
        'flash',
        'bindscancode',
        'binderror'
    ], {
        layoutRef
    });
    if (navigation && navigation.camera && navigation.camera !== camera) {
        warn('<camera>: 一个页面只能插入一个');
        return null;
    }
    else if (navigation) {
        navigation.camera = camera;
    }
    if (!hasPermission || !device) {
        return null;
    }
    return createElement(Camera, innerProps);
});
_camera.displayName = 'MpxCamera';
export default _camera;
