/*!
 * dark-mode.ts
 * Source: https://dbohdan.com/assets/dark-mode.ts
 *
 * Toggle dark-mode CSS.
 *
 * Copyright (c) 2025 D. Bohdan.
 * License: MIT.
 * https://dbohdan.com/mit-license/2025
 */

"use strict";

type Theme = "system" | "reverse" | "light" | "dark" | "flicker";

const darkMode = {
    ID: "dark-mode-css",
    STORAGE_KEY_FLICKER: "dark-mode-flicker",
    STORAGE_KEY_THEME: "dark-mode-theme",

    THEME_DARK: "dark",
    THEME_LIGHT: "light",
    THEME_SYSTEM: "system",
    THEME_FLICKER: "flicker",

    MEDIA_QUERIES: {
        "dark": "screen",
        "reverse": "screen and (prefers-color-scheme: light)",
        "light": "not all",
        "system": "screen and (prefers-color-scheme: dark)",
    },

    isDarkMode(): boolean {
        const el = this.getCSSElement();

        if (!el || !window.matchMedia) {
            return false;
        }

        return window.matchMedia(el.media).matches;
    },

    getFlicker(): boolean {
        return JSON.parse(
            localStorage.getItem(this.STORAGE_KEY_FLICKER) || "true",
        );
    },

    updateFlicker(): void {
        const state = !this.isDarkMode();
        localStorage.setItem(this.STORAGE_KEY_FLICKER, JSON.stringify(state));
    },

    getCSSElement(): HTMLStyleElement | null {
        return document.getElementById(this.ID) as HTMLStyleElement | null;
    },

    mediaQuery(theme: Theme): string {
        if (theme === this.THEME_FLICKER) {
            theme = this.getFlicker()
                ? (this.THEME_DARK as Theme)
                : (this.THEME_LIGHT as Theme);
        }

        if (!this.MEDIA_QUERIES.hasOwnProperty(theme)) {
            throw new Error(`Unrecognized theme: ${theme}`);
        }

        return this.MEDIA_QUERIES[theme as keyof typeof this.MEDIA_QUERIES];
    },

    setTheme(theme: Theme): void {
        const el = this.getCSSElement();
        if (el) {
            el.media = this.mediaQuery(theme);
        }

        this.saveTheme(theme);

        const update = () => {
            this.updateFlicker();
        };

        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", update);
        } else {
            update();
        }
    },

    loadTheme(): Theme {
        return (localStorage.getItem(this.STORAGE_KEY_THEME) ||
            this.THEME_SYSTEM) as Theme;
    },

    saveTheme(theme: Theme): void {
        // Validate the theme.
        this.mediaQuery(theme);

        localStorage.setItem(this.STORAGE_KEY_THEME, theme);
    },

    init(): void {
        const theme = this.loadTheme() || (this.THEME_SYSTEM as Theme);

        this.setTheme(theme);
    },
};

darkMode.init();
