import { createContext, useEffect, useMemo } from 'react'
import useLocalStorageState from 'use-local-storage-state'

import { createUseContext } from '@lib/react'

const ALLOWED_BRANDS = ['client', 'default'] as const

export type ColorMode = 'dark' | 'light' | 'system'
export type Brand = (typeof ALLOWED_BRANDS)[number]
export type Theme = `${Brand} ${ColorMode}`
type ResolvedColorMode = 'dark' | 'light'

type ThemeProviderProps = {
  children: React.ReactNode
  defaultBrand?: Brand
  defaultColorMode?: ColorMode
  storageKey?: string
}

type ThemeProviderState = {
  brand: Brand
  colorMode: ColorMode
  setColorMode: (colorMode: ColorMode) => void
  setBrand: (brand: Brand) => void
}

type ThemeStorage = {
  brand: Brand
  colorMode: ColorMode
}

const initialState: ThemeProviderState = {
  brand: 'default',
  colorMode: 'system',
  setColorMode: () => null,
  setBrand: () => null,
}

const ThemeContext = createContext<ThemeProviderState>(initialState)
const STORAGE_KEY = 'PXL-theme-mode'

export function ThemeProvider({
  children,
  defaultBrand = 'default',
  defaultColorMode = 'system',
  ...props
}: Readonly<ThemeProviderProps>) {
  const [themeStorage, setThemeStorage] = useLocalStorageState<ThemeStorage>(STORAGE_KEY, {
    defaultValue: { brand: defaultBrand, colorMode: defaultColorMode },
  })
  const { brand, colorMode } = themeStorage

  useEffect(() => {
    const root = globalThis.document.documentElement

    root.classList.remove('light', 'dark', ...ALLOWED_BRANDS)
    root.classList.add(brand)

    let resolvedColorMode: ResolvedColorMode
    if (colorMode === 'system') {
      resolvedColorMode = globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
      setThemeStorage({ brand, colorMode: resolvedColorMode })
      root.classList.add(resolvedColorMode)
      return
    }

    resolvedColorMode = colorMode
    root.classList.add(resolvedColorMode)
  }, [brand, colorMode, setThemeStorage])

  const value = useMemo(
    () => ({
      brand,
      colorMode,
      setColorMode: (newColorMode: ColorMode) => {
        setThemeStorage({ brand, colorMode: newColorMode })
      },
      setBrand: (newBrand: Brand) => {
        setThemeStorage({ brand: newBrand, colorMode })
      },
    }),
    [brand, colorMode, setThemeStorage],
  )

  return (
    <ThemeContext.Provider {...props} value={value}>
      {children}
    </ThemeContext.Provider>
  )
}

export const useTheme = createUseContext(ThemeContext, 'ThemeProviderContext')
