import { PropsWithChildren, useContext, useEffect, useMemo, useState } from 'react';

import { ThemedChildren } from './ThemedChildren';
import type { Theming } from './const';
import { DEFAULT_BASE_THEME, DEFAULT_SCREEN_MODE } from './const';
import { getThemeClassName, normalizeTheme } from './helpers';
import { ThemeContext } from './ThemeProviderContext';

export type ThemeProviderProps = PropsWithChildren<Theming> & { className?: string };

// RegEx to check for `np-theme-` class name
const themeClass = /\bnp-theme-[a-z-]+\b/g;

export const ThemeProvider = ({
  theme: initialTheme = DEFAULT_BASE_THEME,
  screenMode: initialScreenMode = DEFAULT_SCREEN_MODE,
  isNotRootProvider: isLocal = false,
  children,
  className = undefined,
}: ThemeProviderProps) => {
  const isContextRoot = useContext(ThemeContext) === undefined;
  const [theme, setTheme] = useState(normalizeTheme(initialTheme));
  const [screenMode, setScreenMode] = useState(initialScreenMode);

  // Update state when props change (for controlled usage)
  useEffect(() => {
    setTheme(normalizeTheme(initialTheme));
  }, [initialTheme]);

  useEffect(() => {
    setScreenMode(initialScreenMode);
  }, [initialScreenMode]);

  // useEffect hook used to apply the theme class to the HTML element
  useEffect(() => {
    if (!isLocal && isContextRoot) {
      // Remove all the theme classes from the documentElement
      document.documentElement.className.match(themeClass)?.forEach((item) => {
        document.documentElement.classList.remove(item);
      });
      getThemeClassName(theme, screenMode)
        .split(' ')
        .forEach((item) => {
          document.documentElement.classList.add(item);
        });
    }
  }, [isLocal, isContextRoot, theme, screenMode]);

  const contextValue = useMemo(
    () => ({ theme, screenMode, setTheme, setScreenMode }),
    [theme, screenMode, setTheme, setScreenMode],
  );

  return (
    <ThemeContext.Provider value={contextValue}>
      <ThemedChildren className={className}>{children}</ThemedChildren>
    </ThemeContext.Provider>
  );
};
