'use client';

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

const META_THEME_COLORS = {
  light: '#ffffff',
  dark: '#09090b'
} as const;

const STORAGE_KEY = 'theme';
const COOKIE_NAME = 'theme_mode';
const DEFAULT_THEMES = ['light', 'dark'] as const;
const MEDIA_QUERY = '(prefers-color-scheme: dark)';

type ThemeMode = 'light' | 'dark' | 'system';

type ThemeProviderProps = PropsWithChildren<{
  attribute?: 'class' | `data-${string}`;
  defaultTheme?: ThemeMode;
  disableTransitionOnChange?: boolean;
  enableColorScheme?: boolean;
  enableSystem?: boolean;
  forcedTheme?: ThemeMode | null;
  storageKey?: string;
  themes?: string[];
}>;

type ThemeContextValue = {
  forcedTheme?: ThemeMode | null;
  resolvedTheme: 'light' | 'dark';
  setTheme: (theme: ThemeMode) => void;
  systemTheme: 'light' | 'dark';
  theme: ThemeMode;
  themes: string[];
};

const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);

function getSystemTheme() {
  if (typeof window === 'undefined') {
    return 'light' as const;
  }

  return window.matchMedia(MEDIA_QUERY).matches ? 'dark' : 'light';
}

function getInitialTheme(defaultTheme: ThemeMode, storageKey: string) {
  if (typeof window === 'undefined') {
    return defaultTheme;
  }

  const savedTheme = window.localStorage.getItem(storageKey);
  if (savedTheme === 'light' || savedTheme === 'dark' || savedTheme === 'system') {
    return savedTheme;
  }

  return defaultTheme;
}

function setCookie(name: string, value: string) {
  document.cookie = `${name}=${value}; path=/; max-age=31536000; SameSite=Lax; ${
    window.location.protocol === 'https:' ? 'Secure;' : ''
  }`;
}

function disableTransitions() {
  const style = document.createElement('style');
  style.appendChild(
    document.createTextNode(
      '*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}'
    )
  );

  document.head.appendChild(style);

  return () => {
    window.getComputedStyle(document.body);
    window.setTimeout(() => {
      document.head.removeChild(style);
    }, 1);
  };
}

function ThemeColorSync({ resolvedTheme }: { resolvedTheme: 'light' | 'dark' }) {
  useEffect(() => {
    const metaThemeColor = document.querySelector('meta[name="theme-color"]');
    metaThemeColor?.setAttribute('content', META_THEME_COLORS[resolvedTheme]);
  }, [resolvedTheme]);

  return null;
}

export default function ThemeProvider({
  attribute = 'class',
  children,
  defaultTheme = 'system',
  disableTransitionOnChange = false,
  enableColorScheme = false,
  enableSystem = false,
  forcedTheme = null,
  storageKey = STORAGE_KEY,
  themes = [...DEFAULT_THEMES]
}: ThemeProviderProps) {
  const [theme, setThemeState] = useState<ThemeMode>(() =>
    getInitialTheme(defaultTheme, storageKey)
  );
  const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(getSystemTheme);
  const resolvedTheme =
    forcedTheme && forcedTheme !== 'system'
      ? forcedTheme
      : theme === 'system' && enableSystem
        ? systemTheme
        : theme === 'dark'
          ? 'dark'
          : 'light';

  useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    const mediaQuery = window.matchMedia(MEDIA_QUERY);
    const updateSystemTheme = () => {
      setSystemTheme(mediaQuery.matches ? 'dark' : 'light');
    };

    updateSystemTheme();
    mediaQuery.addEventListener('change', updateSystemTheme);

    return () => {
      mediaQuery.removeEventListener('change', updateSystemTheme);
    };
  }, []);

  useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    const syncTheme = (event: StorageEvent) => {
      if (event.key !== storageKey) {
        return;
      }

      if (event.newValue === 'light' || event.newValue === 'dark' || event.newValue === 'system') {
        setThemeState(event.newValue);
        return;
      }

      setThemeState(defaultTheme);
    };

    window.addEventListener('storage', syncTheme);

    return () => {
      window.removeEventListener('storage', syncTheme);
    };
  }, [defaultTheme, storageKey]);

  useEffect(() => {
    const root = document.documentElement;
    const restoreTransitions = disableTransitionOnChange ? disableTransitions() : undefined;
    const attributeValues = enableSystem ? [...themes, 'system'] : themes;

    if (attribute === 'class') {
      root.classList.remove(...attributeValues);
      root.classList.add(resolvedTheme);
    } else {
      root.setAttribute(attribute, resolvedTheme);
    }

    if (enableColorScheme) {
      root.style.colorScheme = resolvedTheme;
    }

    restoreTransitions?.();
  }, [attribute, disableTransitionOnChange, enableColorScheme, enableSystem, resolvedTheme, themes]);

  useEffect(() => {
    if (forcedTheme) {
      return;
    }

    window.localStorage.setItem(storageKey, theme);
    setCookie(COOKIE_NAME, theme);
  }, [forcedTheme, storageKey, theme]);

  return (
    <ThemeContext.Provider
      value={{
        forcedTheme,
        resolvedTheme,
        setTheme: setThemeState,
        systemTheme,
        theme: forcedTheme ?? theme,
        themes: enableSystem ? [...themes, 'system'] : themes
      }}
    >
      <ThemeColorSync resolvedTheme={resolvedTheme} />
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);

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

  return context;
}
