import { createContext, useCallback, use, useEffect, useMemo, useState, useRef } from 'react';
import {
  GlobalStyles,
  PaletteMode,
  StyledEngineProvider,
  ThemeProvider as MuiThemeProvider,
  Theme,
  ThemeOptions,
  useTheme,
  CssBaseline,
} from '@mui/material';
import { deepmerge } from '@mui/utils';
import set from 'lodash/set';
import isUndefined from 'lodash/isUndefined';
import { useCreation, useDebounceFn } from 'ahooks';
import {
  BLOCKLET_THEME_PREFER_KEY,
  getDefaultThemePrefer,
  isValidThemeMode,
  getBlockletThemeOptions,
  type BlockletThemeMeta,
} from '@blocklet/theme';

import { useLocationState } from '../hooks/use-location-state';
import { createTheme, isTheme, isUxTheme, lazyCreateDefaultTheme, type UxThemeOptions } from './theme';

const defaultTheme = createTheme();

/** 颜色模式上下文类型 */
export interface ColorSchemeContextType {
  mode: PaletteMode;
  toggleMode: () => void;
  changeMode: (mode: PaletteMode) => void;
  prefer?: Prefer;
}
export type UxTheme = ThemeOptions | ((parentTheme: Theme, context?: { mode: PaletteMode }) => Theme);
export type Prefer = 'light' | 'dark' | 'system';

export const ColorSchemeContext = createContext<ColorSchemeContextType>({} as ColorSchemeContextType);
export function useColorScheme() {
  return use(ColorSchemeContext);
}

/** 根据偏好获取颜色模式 */
const resolveMode = (prefer?: Prefer): PaletteMode => {
  // 允许组件的 prefer 属性覆盖 blocklet theme 中配置的 prefer
  if (prefer) {
    if (prefer === 'system') {
      return getDefaultThemePrefer({ theme: { prefer: 'system' } });
    }
    return prefer;
  }

  return getDefaultThemePrefer();
};

/** 深色模式全局样式 */
function DarkSchemeStyles({ className = undefined }: { className?: string }) {
  const theme = useTheme();

  if (theme.palette.mode === 'dark') {
    const trackColor = 'transparent';
    const thumbColor = theme.palette.grey[300];

    // 处理作用域选择器
    const prefix = (className || '')
      .trim()
      .split(/\s+/)
      .filter(Boolean)
      .map((c) => (c.startsWith('.') ? c : `.${c}`))
      .join(' ');

    const webkitScrollbar = prefix
      ? `${prefix}::-webkit-scrollbar, ${prefix} *::-webkit-scrollbar`
      : '*::-webkit-scrollbar';
    const webkitTrack = prefix
      ? `${prefix}::-webkit-scrollbar-track, ${prefix} *::-webkit-scrollbar-track`
      : '*::-webkit-scrollbar-track';
    const webkitThumb = prefix
      ? `${prefix}::-webkit-scrollbar-thumb, ${prefix} *::-webkit-scrollbar-thumb`
      : '*::-webkit-scrollbar-thumb';
    const webKitCorner = prefix
      ? `${prefix}::-webkit-scrollbar-corner, ${prefix} *::-webkit-scrollbar-corner`
      : '*::-webkit-scrollbar-corner';
    const firefoxSelector = prefix ? `${prefix}, ${prefix} *` : '*';

    return (
      <GlobalStyles
        styles={{
          // Chrome, Safari, Edge
          '@supports selector(::-webkit-scrollbar)': {
            [webkitScrollbar]: {
              width: '12px',
              height: '12px',
            },
            [webkitTrack]: {
              background: trackColor,
            },
            [webkitThumb]: {
              background: thumbColor,
              borderRadius: '6px',
              border: '2px solid',
              borderColor: trackColor,
              backgroundClip: 'padding-box',
              '&:hover': {
                background: theme.palette.grey[400],
                backgroundClip: 'padding-box',
              },
            },
            [webKitCorner]: {
              background: trackColor,
            },
          },
          // Firefox
          '@supports not selector(::-webkit-scrollbar)': {
            [firefoxSelector]: {
              scrollbarWidth: 'auto',
              scrollbarColor: `${thumbColor} ${trackColor}`,
            },
          },
        }}
      />
    );
  }

  return null;
}

let injectedCssBaseline = false;
const injectedDarkSchemeStylesMap: Record<string, boolean> = {};

interface BaseThemeProviderProps {
  children?: React.ReactNode;
  theme?: UxTheme;
  injectFirst?: boolean;
  /** 指定一个类名，DarkSchemeStyles 只会作用于带有该类的元素及其后代 */
  darkSchemeClass?: string;
  /** 禁用样式缓存 */
  disableStyleCache?: boolean;
}
/** 快速配置 MUI 主题 */
function BaseThemeProvider({
  children = null,
  theme = defaultTheme,
  injectFirst = true,
  darkSchemeClass = '',
  disableStyleCache = false,
}: BaseThemeProviderProps) {
  const _theme = useMemo(() => {
    if (typeof theme === 'function') {
      return theme;
    }

    if (isTheme(theme)) return theme;

    // 是 ThemeOptions 则创建一个 theme
    return createTheme(theme);
  }, [theme]);

  const cssBaseline = useCreation(() => {
    if (disableStyleCache) return <CssBaseline />;

    if (injectedCssBaseline) return null;
    injectedCssBaseline = true;
    return <CssBaseline />;
  }, [disableStyleCache]);

  const darkSchemeStyles = useCreation(() => {
    if (disableStyleCache) return <DarkSchemeStyles className={darkSchemeClass} />;

    if (injectedDarkSchemeStylesMap[darkSchemeClass]) return null;
    injectedDarkSchemeStylesMap[darkSchemeClass] = true;
    return <DarkSchemeStyles className={darkSchemeClass} />;
  }, [darkSchemeClass, disableStyleCache]);

  return (
    // injectFirst 会影响 makeStyles 自定义样式和 mui styles 覆盖问题
    <StyledEngineProvider injectFirst={injectFirst}>
      <MuiThemeProvider theme={_theme}>
        {cssBaseline}
        {darkSchemeStyles}
        {children}
      </MuiThemeProvider>
    </StyledEngineProvider>
  );
}

interface ColorSchemeProviderProps extends BaseThemeProviderProps {
  prefer?: Prefer;
  disableBlockletTheme?: boolean;
}
/** 配置带颜色模式切换功能的 MUI 主题 */
function ColorSchemeProvider({
  children = null,
  theme: themeInput = undefined,
  prefer = undefined,
  disableBlockletTheme = false,
  ...rest
}: ThemeProviderProps) {
  const parentColorScheme = useColorScheme();
  const [mode, setMode] = useState<PaletteMode>(() => resolveMode(prefer));
  const [themeBuilderConfig, setThemeBuilderConfig] = useState<BlockletThemeMeta | null>(null);
  const parentTheme = useTheme();
  const location = useLocationState();
  const metaThemeColorRef = useRef<HTMLMetaElement | null>(null);
  const isTopColorScheme = isUndefined(parentColorScheme.toggleMode);

  // 使用防抖函数包装 setThemeBuilderConfig，避免过于频繁的主题修改
  const { run: debouncedSetThemeBuilderConfig } = useDebounceFn(
    (config: BlockletThemeMeta | null) => {
      setThemeBuilderConfig(config);
    },
    {
      wait: 200,
    }
  );

  const _themeInput = useMemo(() => {
    let result: UxThemeOptions = {};

    if (themeInput) {
      const createBaseTheme = lazyCreateDefaultTheme(mode);

      if (typeof themeInput === 'function') {
        const baseTheme = createBaseTheme();

        if (isUxTheme(parentTheme)) {
          result = { ...themeInput(parentTheme, { mode }) };
        } else {
          result = { ...themeInput(baseTheme, { mode }) };
        }
      } else {
        result = { ...themeInput };
      }
    }

    // 接受 ThemeBuilder 配置
    let _mode = mode;
    if (themeBuilderConfig) {
      _mode = themeBuilderConfig.mode || 'light';
      result = deepmerge(result, getBlockletThemeOptions(_mode, themeBuilderConfig));
    }

    set(result, 'palette.mode', _mode);
    set(result, 'mode', _mode);

    return result;
  }, [mode, themeInput, parentTheme, themeBuilderConfig]);

  const theme = useMemo(() => {
    return createTheme({ ..._themeInput, disableBlockletTheme: !!themeBuilderConfig || disableBlockletTheme });
  }, [_themeInput, disableBlockletTheme, themeBuilderConfig]);

  // 切换明/暗模式
  const toggleMode = useCallback(() => {
    const newMode = mode === 'light' ? 'dark' : 'light';
    setMode(newMode);
    sessionStorage.removeItem(BLOCKLET_THEME_PREFER_KEY);
    localStorage.setItem(BLOCKLET_THEME_PREFER_KEY, newMode);
  }, [mode, setMode]);

  const changeMode = useCallback(
    (newMode: PaletteMode) => {
      if (mode !== newMode) {
        setMode(newMode);
        sessionStorage.removeItem(BLOCKLET_THEME_PREFER_KEY);
        localStorage.setItem(BLOCKLET_THEME_PREFER_KEY, newMode);
      }
    },
    [mode, setMode]
  );

  const colorSchemeValue = useMemo(
    () => ({
      mode,
      toggleMode,
      changeMode,
      prefer,
    }),
    [mode, prefer, toggleMode, changeMode]
  );

  // 相应 prefer 或 urlTheme 的改变
  useEffect(() => {
    setMode(resolveMode(prefer));
  }, [prefer, setMode, location.search]);

  // 会话缓存 urlTheme 便于 App Launching 阶段临时统一 theme mode
  useEffect(() => {
    const urlTheme = new URLSearchParams(location.search).get('theme');

    if (isValidThemeMode(urlTheme)) {
      sessionStorage.setItem(BLOCKLET_THEME_PREFER_KEY, urlTheme);
    }
  }, [location.search]);

  // 动态设置 <meta name="theme-color" content="..."> 标签，用于控制移动端浏览器状态栏颜色
  useEffect(() => {
    if (!metaThemeColorRef.current) {
      metaThemeColorRef.current = document.querySelector('meta[name="theme-color"]');
    }

    const bgcolor = theme.palette.background.default;

    if (metaThemeColorRef.current) {
      metaThemeColorRef.current.setAttribute('content', bgcolor);
    } else {
      const meta = document.createElement('meta');
      meta.name = 'theme-color';
      meta.content = bgcolor;
      document.head.appendChild(meta);
      metaThemeColorRef.current = meta;
    }
  }, [theme.palette.background.default]);

  // 监听来自 ThemeBuilder 的消息，支持 Blocklet 实时预览
  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      // 必须同源
      if (event.origin !== window.origin) return;

      if (event.data.type === 'THEME_BUILDER_CONFIG_CHANGED') {
        debouncedSetThemeBuilderConfig(event.data.payload);
      }
    };

    window.addEventListener('message', handleMessage);

    return () => window.removeEventListener('message', handleMessage);
  }, [debouncedSetThemeBuilderConfig]);

  // 修改 document[data-theme] 属性
  useEffect(() => {
    if (isTopColorScheme) {
      if (mode === 'dark') {
        document.documentElement.setAttribute('data-theme', 'dark');
      } else {
        document.documentElement.removeAttribute('data-theme');
      }
    }
  }, [isTopColorScheme, mode]);

  return (
    <ColorSchemeContext value={colorSchemeValue}>
      <BaseThemeProvider theme={theme} {...rest}>
        {children}
      </BaseThemeProvider>
    </ColorSchemeContext>
  );
}

export interface ThemeProviderProps extends ColorSchemeProviderProps {
  /** 下列情况会启用 ColorScheme 功能（让 theme 支持明暗模式切换）
   * 1. 显示打开 enableColorScheme
   * 2. 显示设置 prefer
   * 3. 顶层 ThemeProvider
   */
  enableColorScheme?: boolean;
}
export default function ThemeProvider({
  children = null,
  prefer = undefined,
  enableColorScheme = false,
  ...props
}: ThemeProviderProps) {
  const { toggleMode } = useColorScheme();

  if (enableColorScheme || prefer || !toggleMode) {
    return (
      <ColorSchemeProvider prefer={prefer} {...props}>
        {children}
      </ColorSchemeProvider>
    );
  }

  return <BaseThemeProvider {...props}>{children}</BaseThemeProvider>;
}
