import type { UnistylesBreakpoints, UnistylesThemes } from './global'
import type { UnistylesNavigationBar } from './specs/NavigtionBar'
import type { UnistylesStatusBar } from './specs/StatusBar'
import type { UnistylesConfig, UnistylesStyleSheet } from './specs/StyleSheet'
import type { ColorScheme, Orientation } from './specs/types'
import type { UnistylesRuntimePrivate } from './specs/UnistylesRuntime'
import type { IOSContentSizeCategory, UnistylesTheme } from './types'

type Registry = {
    themes: UnistylesThemes
    breakpoints: UnistylesBreakpoints
}

jest.mock('react-native-nitro-modules', () => ({
    NitroModules: {
        createHybridObject: () => ({
            add: () => {},
            init: () => {},
            createHybridStatusBar: () => ({
                setStyle: () => {},
            }),
            createHybridNavigationBar: () => {},
        }),
    },
}))

jest.mock('react-native-unistyles', () => {
    const React = require('react')
    const _REGISTRY: Registry = {
        themes: {},
        breakpoints: {},
    }
    const miniRuntime = {
        themeName: undefined,
        breakpoint: undefined,
        hasAdaptiveThemes: false,
        colorScheme: 'unspecified' as ColorScheme,
        contentSizeCategory: 'Medium' as IOSContentSizeCategory,
        insets: {
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            ime: 0,
        },
        pixelRatio: 1,
        fontScale: 1,
        rtl: false,
        isLandscape: false,
        isPortrait: true,
        navigationBar: {
            width: 0,
            height: 0,
        },
        screen: {
            width: 0,
            height: 0,
        },
        statusBar: {
            width: 0,
            height: 0,
        },
    }
    const unistylesRuntime = {
        colorScheme: 'unspecified' as ColorScheme,
        contentSizeCategory: 'Medium' as IOSContentSizeCategory,
        orientation: 'portrait' as Orientation,
        isPortrait: true,
        isLandscape: false,
        breakpoints: {},
        dispose: () => {},
        equals: () => false,
        name: 'UnistylesRuntimeMock',
        miniRuntime: miniRuntime,
        statusBar: {
            height: 0,
            width: 0,
            name: 'StatusBarMock',
            equals: () => false,
            setHidden: () => {},
            setStyle: () => {},
        },
        navigationBar: {
            height: 0,
            width: 0,
            name: 'NavigationBarMock',
            equals: () => false,
            setHidden: () => {},
            dispose: () => {},
        },
        fontScale: 1,
        hasAdaptiveThemes: false,
        pixelRatio: 0,
        rtl: false,
        getTheme: () => {
            return (Object.values(_REGISTRY.themes).at(0) ?? {}) as UnistylesTheme
        },
        setTheme: () => {},
        updateTheme: () => {},
        setRootViewBackgroundColor: () => {},
        nativeSetRootViewBackgroundColor: () => {},
        createHybridStatusBar: () => {
            return {} as UnistylesStatusBar
        },
        createHybridNavigationBar: () => {
            return {} as UnistylesNavigationBar
        },
        setAdaptiveThemes: () => {},
        setImmersiveMode: () => {},
        setImmersiveModeNative: () => {},
        insets: {
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            ime: 0,
        },
        screen: {
            width: 0,
            height: 0,
        },
        breakpoint: undefined,
    } satisfies UnistylesRuntimePrivate

    const stripVariants = (styleEntries: Record<string, any>): any => {
        const result: Record<string, any> = {}

        for (const [name, style] of Object.entries(styleEntries)) {
            if (typeof style === 'function') {
                result[name] = (...args: Array<any>) => {
                    const resolved = style(...args)
                    const { variants, compoundVariants, ...rest } = resolved

                    return rest
                }
            } else if (style !== null && typeof style === 'object' && !Array.isArray(style)) {
                const { variants, compoundVariants, ...rest } = style

                result[name] = rest
            } else {
                result[name] = style
            }
        }

        return result
    }

    return {
        Hide: () => null,
        Display: () => null,
        ScopedTheme: () => null,
        withUnistyles:
            <TComponent>(
                Component: TComponent,
                mapper?: (theme: UnistylesTheme, runtime: typeof miniRuntime) => TComponent,
            ) =>
            (props: any) =>
                React.createElement(Component, {
                    ...mapper?.((Object.values(_REGISTRY.themes).at(0) ?? {}) as UnistylesTheme, miniRuntime),
                    ...props,
                }),
        mq: {
            only: {
                width: () => ({
                    and: {
                        height: () => ({}),
                    },
                }),
                height: () => ({
                    and: {
                        width: () => ({}),
                    },
                }),
            },
            width: () => ({
                and: {
                    height: () => ({}),
                },
            }),
            height: () => ({
                and: {
                    width: () => ({}),
                },
            }),
        },
        useUnistyles: () => ({
            theme: Object.values(_REGISTRY.themes).at(0) ?? {},
            rt: unistylesRuntime,
        }),
        StyleSheet: {
            absoluteFillObject: {
                position: 'absolute',
                left: 0,
                right: 0,
                top: 0,
                bottom: 0,
            },
            absoluteFill: {
                position: 'absolute',
                left: 0,
                right: 0,
                top: 0,
                bottom: 0,
            } as unknown as UnistylesStyleSheet['absoluteFill'],
            compose: (styles: any) => {
                return styles
            },
            flatten: (styles: any) => {
                return styles
            },
            create: (styles: any) => {
                const resolved =
                    typeof styles === 'function'
                        ? styles(Object.values(_REGISTRY.themes).at(0) ?? {}, miniRuntime)
                        : styles

                return {
                    ...stripVariants(resolved),
                    useVariants: () => {},
                }
            },
            configure: (config: UnistylesConfig) => {
                if (config.breakpoints) {
                    _REGISTRY.breakpoints = config.breakpoints
                }

                if (config.themes) {
                    _REGISTRY.themes = config.themes
                }
            },
            jsMethods: {
                processColor: () => null,
                parseBoxShadowString: () => [],
            },
            hairlineWidth: 1,
            addChangeListener: () => () => {},
            init: () => {},
            name: 'StyleSheetMock',
            dispose: () => {},
            equals: () => false,
        } satisfies UnistylesStyleSheet,
        UnistylesRuntime: unistylesRuntime,
    }
})

jest.mock('react-native-unistyles/reanimated', () => {
    const unistyles = require('react-native-unistyles')
    const mockedSharedValue = (value: any) => ({
        get: () => value,
        set: (_value: any) => {},
        value,
    })

    return {
        useAnimatedTheme: () => {
            const theme = unistyles.useUnistyles().theme
            const sharedTheme = mockedSharedValue(theme)

            return sharedTheme
        },
        useAnimatedVariantColor: () => {
            const fromValue = mockedSharedValue('#000000')
            const toValue = mockedSharedValue('#FFFFFF')

            return {
                fromValue,
                toValue,
            }
        },
    }
})
