import React, {
  createContext,
  PropsWithChildren,
  useCallback,
  useEffect,
  useState,
} from 'react';
import {
  Appearance,
  Dimensions,
  StyleSheet,
  View,
  useWindowDimensions,
} from 'react-native';

import { fonts, fontSizes, titleFontSizes } from './fonts';
import {
  colorsDark,
  colorsLight,
  zIndices,
  sizes,
  borderRadius,
} from './defaultValues';
import {
  FontTypes,
  ThemeContextProps,
  ThemeContextValue,
  ThemeProps,
} from './types';
import { ColorName, ColorSchema } from './color.type';
import { getColorValue } from './utils';
import { useSetThemeBody } from '../../hooks/useSetThemeBody';
import { ScreenLoadingProvider } from '../screen-loading';

const dimensions = Dimensions.get('screen');

const colorScheme = Appearance.getColorScheme();

const initialColorFunction = (
  colors: ColorSchema
): ThemeContextValue['colors'] => {
  return {
    ...colors,
    get: (color: ColorName) => {
      return getColorValue(color, colors);
    },
  };
};

type InternalStateTheme = ThemeProps;

const initialValue: InternalStateTheme = {
  theme: colorScheme === 'dark' ? 'dark' : 'light',
  isDark: colorScheme === 'dark',
  fonts: fonts as FontTypes,
  fontSizes,
  width: dimensions.width,
  height: dimensions.height,
  titleFontSizes,
  activeOpacity: 0.6,
  zIndices,
  sizes,
  spacing: 8,
  borderWidth: 1,
  darkColors: colorsDark,
  lightColors: colorsLight,
  borderRadius,
  colors:
    colorScheme === 'dark'
      ? initialColorFunction(colorsDark)
      : initialColorFunction(colorsLight),
};

const themeContextDefaultValue: ThemeContextValue = {
  ...initialValue,
  maxHeight: dimensions.height,
  maxWidth: dimensions.width,
  setContextTheme: () => {},
  setTheme: () => {},
};

export const ThemeContext = createContext<ThemeContextValue>(
  themeContextDefaultValue
);

export const resolveSize = (
  maxValue: number,
  value?: ThemeContextProps['width']
) => {
  if (typeof value === 'function') {
    return value(maxValue);
  }
  return value || undefined;
};

export const ThemeProvider: React.FC<PropsWithChildren<ThemeContextProps>> =
  React.memo(({ children, onChangeTheme, theme, ...props }) => {
    const { width, height } = useWindowDimensions();

    const [internalTheme, setInternalTheme] = useState<InternalStateTheme>({
      ...initialValue,
    });

    const setContextTheme = React.useCallback(
      (_theme: ThemeContextProps['theme']) => {
        setInternalTheme((prevTheme) => {
          const themeScheme = _theme?.theme ?? prevTheme?.theme;
          const darkColors = Object.assign(
            prevTheme.darkColors,
            _theme?.colors?.dark
          );
          const lightColors = Object.assign(
            prevTheme.lightColors,
            _theme?.colors?.light
          );

          const colors = initialColorFunction(
            Object.assign(
              themeScheme === 'dark' ? darkColors : lightColors,
              _theme?.colors?.[themeScheme]
            )
          );

          const _borderRadius = Object.assign(
            prevTheme?.borderRadius,
            _theme?.borderRadius
          );

          // createColorName(colors);

          return {
            colors,
            theme: themeScheme,
            darkColors,
            lightColors,
            width: _theme?.width ?? width,
            height: _theme?.height ?? height,
            borderRadius: _borderRadius,
            isDark: themeScheme === 'dark',
            borderWidth: _theme?.borderWidth ?? prevTheme.borderWidth,
            activeOpacity: _theme?.activeOpacity ?? prevTheme.activeOpacity,
            spacing: _theme?.spacing ?? prevTheme.spacing,
            fonts: Object.assign(prevTheme?.fonts, _theme?.fonts),
            sizes: Object.assign(prevTheme?.sizes, _theme?.sizes),
            fontSizes: Object.assign(prevTheme?.fontSizes, _theme?.fontSizes),
            titleFontSizes: Object.assign(
              prevTheme?.titleFontSizes,
              _theme?.titleFontSizes
            ),
            zIndices: Object.assign(prevTheme?.zIndices, _theme?.zIndices),
          };
        });
      },
      [height, width]
    );

    const setTheme = useCallback((_theme: 'dark' | 'light') => {
      setInternalTheme((prevTheme) => {
        return {
          ...prevTheme,
          theme: _theme,
          isDark: _theme === 'dark',
          colors: initialColorFunction(
            _theme === 'dark' ? prevTheme.darkColors : prevTheme.lightColors
          ),
        };
      });
    }, []);

    useEffect(() => {
      setContextTheme(theme || {});
    }, [theme, setContextTheme]);

    useEffect(() => {
      onChangeTheme?.(internalTheme?.theme);
    }, [internalTheme, onChangeTheme]);

    // set the body theme in web
    useSetThemeBody(internalTheme.theme, internalTheme.colors);

    const maxWidth = resolveSize(internalTheme.width, props?.width);
    const maxHeight = resolveSize(internalTheme.height, props?.height);

    return (
      <ThemeContext.Provider
        value={React.useMemo(() => {
          return {
            colors: internalTheme.colors,
            theme: internalTheme.theme,
            darkColors: internalTheme.darkColors,
            lightColors: internalTheme.lightColors,
            isDark: internalTheme.isDark,
            zIndices: internalTheme.zIndices,
            sizes: internalTheme.sizes,
            spacing: internalTheme.spacing,
            fonts: internalTheme.fonts,
            borderRadius: internalTheme.borderRadius,
            fontSizes: internalTheme.fontSizes,
            titleFontSizes: internalTheme.titleFontSizes,
            activeOpacity: internalTheme.activeOpacity,
            borderWidth: internalTheme.borderWidth,
            setContextTheme: setContextTheme,
            setTheme,
            width: internalTheme.width,
            height: internalTheme.height,
            maxHeight,
            maxWidth,
          } as ThemeContextValue;
          // eslint-disable-next-line react-hooks/exhaustive-deps
        }, [internalTheme, setTheme, setContextTheme])}
      >
        <ScreenLoadingProvider {...props}>
          <View
            style={StyleSheet.flatten([
              styles.wrapper,
              {
                marginHorizontal: 'auto',
                width: maxWidth,
                height: maxHeight,
                backgroundColor: internalTheme.colors.background.main,
                overflow: props?.width ? 'hidden' : undefined,
              },
            ])}
          >
            {children}
          </View>
        </ScreenLoadingProvider>
      </ThemeContext.Provider>
    );
  });

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