import {
  createContext,
  createEffect,
  createMemo,
  createSignal,
  onCleanup,
  onMount,
  useContext,
  type Accessor,
  type ParentComponent,
} from 'solid-js'
import type { ThemeMode } from '@/types/domain'

export const THEME_STORAGE_KEY = 'seen-theme-mode'

export type ResolvedTheme = 'dark' | 'light'

const isThemeMode = (value: string | null): value is ThemeMode =>
  value === 'dark' || value === 'light' || value === 'system'

export const resolveTheme = (mode: ThemeMode, prefersDark: boolean): ResolvedTheme => {
  if (mode === 'system') {
    return prefersDark ? 'dark' : 'light'
  }

  return mode
}

export const loadStoredTheme = (storage: Storage | null): ThemeMode => {
  if (!storage) {
    return 'system'
  }

  const storedValue = storage.getItem(THEME_STORAGE_KEY)
  return isThemeMode(storedValue) ? storedValue : 'system'
}

interface ThemeContextValue {
  mode: Accessor<ThemeMode>
  resolved: Accessor<ResolvedTheme>
  setMode: (next: ThemeMode) => void
}

const ThemeContext = createContext<ThemeContextValue>()

export const ThemeProvider: ParentComponent = (props) => {
  const storage = typeof window !== 'undefined' ? window.localStorage : null
  const [mode, setMode] = createSignal<ThemeMode>(loadStoredTheme(storage))
  const [prefersDark, setPrefersDark] = createSignal(false)

  const resolved = createMemo<ResolvedTheme>(() => resolveTheme(mode(), prefersDark()))

  onMount(() => {
    const media = window.matchMedia('(prefers-color-scheme: dark)')
    setPrefersDark(media.matches)

    const listener = (event: MediaQueryListEvent): void => {
      setPrefersDark(event.matches)
    }

    media.addEventListener('change', listener)
    onCleanup(() => media.removeEventListener('change', listener))
  })

  createEffect(() => {
    const themeMode = mode()
    storage?.setItem(THEME_STORAGE_KEY, themeMode)
    document.documentElement.setAttribute('data-theme', resolved())
  })

  return (
    <ThemeContext.Provider
      value={{
        mode,
        resolved,
        setMode,
      }}
    >
      {props.children}
    </ThemeContext.Provider>
  )
}

export const useTheme = (): ThemeContextValue => {
  const context = useContext(ThemeContext)

  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider')
  }

  return context
}
