import React, { type ComponentType, forwardRef, type ComponentProps, type ComponentRef } from 'react'

import type { UnistylesValues } from '../../types'
import type { Mappings } from './types'

import { deepMergeObjects } from '../../utils'
import { getClassName } from '../getClassname'
import { useProxifiedUnistyles } from '../useProxifiedUnistyles'
import { maybeWarnAboutMultipleUnistyles } from '../warn'

// @ts-expect-error
type GenericComponentProps<T> = ComponentProps<T>
// @ts-expect-error
type GenericComponentRef<T> = ComponentRef<T>

export const withUnistyles = <TComponent, TMappings extends GenericComponentProps<TComponent>>(
    Component: TComponent,
    mappings?: Mappings<TMappings>,
) => {
    type TProps = GenericComponentProps<TComponent>
    type PropsWithUnistyles = Partial<TProps> & {
        uniProps?: Mappings<TProps>
    }
    type UnistyleStyles = {
        style?: UnistylesValues
        contentContainerStyle?: UnistylesValues
    }

    return forwardRef<GenericComponentRef<TComponent>, PropsWithUnistyles>((props, ref) => {
        const narrowedProps = props as PropsWithUnistyles & UnistyleStyles
        const styleClassNames = getClassName(narrowedProps.style, true)
        const contentContainerStyleClassNames = getClassName(narrowedProps.contentContainerStyle)
        const { proxifiedRuntime, proxifiedTheme } = useProxifiedUnistyles()

        const { key: mappingsKey, ...mappingsProps } = mappings ? mappings(proxifiedTheme, proxifiedRuntime) : {}
        const { key: uniPropsKey, ...unistyleProps } = narrowedProps.uniProps
            ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime)
            : {}

        const emptyStyles = narrowedProps.style
            ? Object.fromEntries(
                  Object.entries(Object.getOwnPropertyDescriptors(narrowedProps.style))
                      .filter(([key]) => !key.startsWith('unistyles') && !key.startsWith('_'))
                      .map(([key]) => [key, undefined]),
              )
            : undefined

        const combinedProps = {
            ...deepMergeObjects(mappingsProps, unistyleProps, props),
            ...(narrowedProps.style
                ? {
                      // Override default component styles with undefined values to reset them
                      style: emptyStyles,
                  }
                : {}),
            ...(narrowedProps.contentContainerStyle
                ? {
                      contentContainerStyle: contentContainerStyleClassNames,
                  }
                : {}),
        } as any

        // @ts-ignore
        // prettier-ignore
        maybeWarnAboutMultipleUnistyles(narrowedProps.style, `withUnistyles(${Component.displayName ?? Component.name ?? 'Unknown'})`)
        // @ts-ignore
        // prettier-ignore
        maybeWarnAboutMultipleUnistyles(narrowedProps.contentContainerStyle, `withUnistyles(${Component.displayName ?? Component.name ?? 'Unknown'})`)

        const NativeComponent = Component as ComponentType
        const [classNames] = styleClassNames ?? []

        return (
            <div className={classNames?.hash} style={{ display: 'contents' }}>
                <NativeComponent key={uniPropsKey || mappingsKey} {...combinedProps} ref={ref} />
            </div>
        )
    })
}
