import { tokens, darkTokens } from '@jobber/design';
import React__default, { createContext, useSyncExternalStore, useEffect, useContext, useMemo } from 'react';
import { g as getDefaultExportFromCjs } from './_commonjsHelpers-es.js';
import { e as eq_1, c as _Stack } from './identity-es.js';
import { _ as _baseAssignValue } from './_baseAssignValue-es.js';
import { _ as _baseFor } from './_baseFor-es.js';
import { _ as _copyObject, k as keysIn_1, a as _cloneBufferExports, b as _cloneTypedArray, c as _copyArray, d as _initCloneObject, i as isPlainObject_1 } from './keysIn-es.js';
import { b as isArrayLike_1, i as isArguments_1, a as isArray_1, c as isBufferExports, k as isFunction_1, d as isTypedArray_1 } from './isTypedArray-es.js';
import { i as isObjectLike_1, a as isObject_1 } from './isObjectLike-es.js';
import { _ as _baseRest, a as _isIterateeCall } from './_isIterateeCall-es.js';

var baseAssignValue = _baseAssignValue,
    eq = eq_1;

/**
 * This function is like `assignValue` except that it doesn't assign
 * `undefined` values.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignMergeValue$2(object, key, value) {
  if ((value !== undefined && !eq(object[key], value)) ||
      (value === undefined && !(key in object))) {
    baseAssignValue(object, key, value);
  }
}

var _assignMergeValue = assignMergeValue$2;

var isArrayLike = isArrayLike_1,
    isObjectLike = isObjectLike_1;

/**
 * This method is like `_.isArrayLike` except that it also checks if `value`
 * is an object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array-like object,
 *  else `false`.
 * @example
 *
 * _.isArrayLikeObject([1, 2, 3]);
 * // => true
 *
 * _.isArrayLikeObject(document.body.children);
 * // => true
 *
 * _.isArrayLikeObject('abc');
 * // => false
 *
 * _.isArrayLikeObject(_.noop);
 * // => false
 */
function isArrayLikeObject$1(value) {
  return isObjectLike(value) && isArrayLike(value);
}

var isArrayLikeObject_1 = isArrayLikeObject$1;

/**
 * Gets the value at `key`, unless `key` is "__proto__" or "constructor".
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the property to get.
 * @returns {*} Returns the property value.
 */

function safeGet$2(object, key) {
  if (key === 'constructor' && typeof object[key] === 'function') {
    return;
  }

  if (key == '__proto__') {
    return;
  }

  return object[key];
}

var _safeGet = safeGet$2;

var copyObject = _copyObject,
    keysIn$1 = keysIn_1;

/**
 * Converts `value` to a plain object flattening inherited enumerable string
 * keyed properties of `value` to own properties of the plain object.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {Object} Returns the converted plain object.
 * @example
 *
 * function Foo() {
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.assign({ 'a': 1 }, new Foo);
 * // => { 'a': 1, 'b': 2 }
 *
 * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
 * // => { 'a': 1, 'b': 2, 'c': 3 }
 */
function toPlainObject$1(value) {
  return copyObject(value, keysIn$1(value));
}

var toPlainObject_1 = toPlainObject$1;

var assignMergeValue$1 = _assignMergeValue,
    cloneBuffer = _cloneBufferExports,
    cloneTypedArray = _cloneTypedArray,
    copyArray = _copyArray,
    initCloneObject = _initCloneObject,
    isArguments = isArguments_1,
    isArray = isArray_1,
    isArrayLikeObject = isArrayLikeObject_1,
    isBuffer = isBufferExports,
    isFunction = isFunction_1,
    isObject$1 = isObject_1,
    isPlainObject = isPlainObject_1,
    isTypedArray = isTypedArray_1,
    safeGet$1 = _safeGet,
    toPlainObject = toPlainObject_1;

/**
 * A specialized version of `baseMerge` for arrays and objects which performs
 * deep merges and tracks traversed objects enabling objects with circular
 * references to be merged.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {string} key The key of the value to merge.
 * @param {number} srcIndex The index of `source`.
 * @param {Function} mergeFunc The function to merge values.
 * @param {Function} [customizer] The function to customize assigned values.
 * @param {Object} [stack] Tracks traversed source values and their merged
 *  counterparts.
 */
function baseMergeDeep$1(object, source, key, srcIndex, mergeFunc, customizer, stack) {
  var objValue = safeGet$1(object, key),
      srcValue = safeGet$1(source, key),
      stacked = stack.get(srcValue);

  if (stacked) {
    assignMergeValue$1(object, key, stacked);
    return;
  }
  var newValue = customizer
    ? customizer(objValue, srcValue, (key + ''), object, source, stack)
    : undefined;

  var isCommon = newValue === undefined;

  if (isCommon) {
    var isArr = isArray(srcValue),
        isBuff = !isArr && isBuffer(srcValue),
        isTyped = !isArr && !isBuff && isTypedArray(srcValue);

    newValue = srcValue;
    if (isArr || isBuff || isTyped) {
      if (isArray(objValue)) {
        newValue = objValue;
      }
      else if (isArrayLikeObject(objValue)) {
        newValue = copyArray(objValue);
      }
      else if (isBuff) {
        isCommon = false;
        newValue = cloneBuffer(srcValue, true);
      }
      else if (isTyped) {
        isCommon = false;
        newValue = cloneTypedArray(srcValue, true);
      }
      else {
        newValue = [];
      }
    }
    else if (isPlainObject(srcValue) || isArguments(srcValue)) {
      newValue = objValue;
      if (isArguments(objValue)) {
        newValue = toPlainObject(objValue);
      }
      else if (!isObject$1(objValue) || isFunction(objValue)) {
        newValue = initCloneObject(srcValue);
      }
    }
    else {
      isCommon = false;
    }
  }
  if (isCommon) {
    // Recursively merge objects and arrays (susceptible to call stack limits).
    stack.set(srcValue, newValue);
    mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
    stack['delete'](srcValue);
  }
  assignMergeValue$1(object, key, newValue);
}

var _baseMergeDeep = baseMergeDeep$1;

var Stack = _Stack,
    assignMergeValue = _assignMergeValue,
    baseFor = _baseFor,
    baseMergeDeep = _baseMergeDeep,
    isObject = isObject_1,
    keysIn = keysIn_1,
    safeGet = _safeGet;

/**
 * The base implementation of `_.merge` without support for multiple sources.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {number} srcIndex The index of `source`.
 * @param {Function} [customizer] The function to customize merged values.
 * @param {Object} [stack] Tracks traversed source values and their merged
 *  counterparts.
 */
function baseMerge$1(object, source, srcIndex, customizer, stack) {
  if (object === source) {
    return;
  }
  baseFor(source, function(srcValue, key) {
    stack || (stack = new Stack);
    if (isObject(srcValue)) {
      baseMergeDeep(object, source, key, srcIndex, baseMerge$1, customizer, stack);
    }
    else {
      var newValue = customizer
        ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack)
        : undefined;

      if (newValue === undefined) {
        newValue = srcValue;
      }
      assignMergeValue(object, key, newValue);
    }
  }, keysIn);
}

var _baseMerge = baseMerge$1;

var baseRest = _baseRest,
    isIterateeCall = _isIterateeCall;

/**
 * Creates a function like `_.assign`.
 *
 * @private
 * @param {Function} assigner The function to assign values.
 * @returns {Function} Returns the new assigner function.
 */
function createAssigner$1(assigner) {
  return baseRest(function(object, sources) {
    var index = -1,
        length = sources.length,
        customizer = length > 1 ? sources[length - 1] : undefined,
        guard = length > 2 ? sources[2] : undefined;

    customizer = (assigner.length > 3 && typeof customizer == 'function')
      ? (length--, customizer)
      : undefined;

    if (guard && isIterateeCall(sources[0], sources[1], guard)) {
      customizer = length < 3 ? undefined : customizer;
      length = 1;
    }
    object = Object(object);
    while (++index < length) {
      var source = sources[index];
      if (source) {
        assigner(object, source, index, customizer);
      }
    }
    return object;
  });
}

var _createAssigner = createAssigner$1;

var baseMerge = _baseMerge,
    createAssigner = _createAssigner;

/**
 * This method is like `_.assign` except that it recursively merges own and
 * inherited enumerable string keyed properties of source objects into the
 * destination object. Source properties that resolve to `undefined` are
 * skipped if a destination value exists. Array and plain object properties
 * are merged recursively. Other objects and value types are overridden by
 * assignment. Source objects are applied from left to right. Subsequent
 * sources overwrite property assignments of previous sources.
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 0.5.0
 * @category Object
 * @param {Object} object The destination object.
 * @param {...Object} [sources] The source objects.
 * @returns {Object} Returns `object`.
 * @example
 *
 * var object = {
 *   'a': [{ 'b': 2 }, { 'd': 4 }]
 * };
 *
 * var other = {
 *   'a': [{ 'c': 3 }, { 'e': 5 }]
 * };
 *
 * _.merge(object, other);
 * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
 */
var merge = createAssigner(function(object, source, srcIndex) {
  baseMerge(object, source, srcIndex);
});

var merge_1 = merge;

var merge$1 = /*@__PURE__*/getDefaultExportFromCjs(merge_1);

var styles = {"staticThemeProviderWrapper":"P6HrDSaI0Ac-","overrideTokensWrapper":"j-NMCOBL3-E-","spinning":"iq03qkKnXBA-"};

const THEME_CHANGE_EVENT = "atlantis.toggle-theme";

/**
 * `useSyncExternalStore` source for the theme. `data-theme` on the document
 * element is the source of truth: `updateTheme` writes it and dispatches
 * `THEME_CHANGE_EVENT`, which this store subscribes to.
 */
const themeStore = {
    subscribe(onStoreChange) {
        globalThis.window.addEventListener(THEME_CHANGE_EVENT, onStoreChange);
        return () => {
            globalThis.window.removeEventListener(THEME_CHANGE_EVENT, onStoreChange);
        };
    },
    getSnapshot() {
        var _a, _b, _c, _d;
        return ((_d = (_c = (_b = (_a = globalThis.document) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c.theme) !== null && _d !== void 0 ? _d : "light");
    },
    getServerSnapshot() {
        return "light";
    },
};

/**
 * Toggle the theme of the application. If subscribed, the tokens will be updated accordingly.
 * Will add data-theme="<newTheme>" to the <html> element.
 * Will dispatch a custom event "atlantis.toggle-theme" that theme providers subscribe to.
 */
function updateTheme(theme) {
    if (!globalThis.window)
        return;
    globalThis.document.documentElement.setAttribute("data-theme", theme);
    const event = new CustomEvent(THEME_CHANGE_EVENT, {
        detail: { theme },
    });
    globalThis.window.dispatchEvent(event);
}

const atlantisThemeContextDefaultValues = {
    theme: "light",
    tokens: tokens,
};
const actualDarkTokens = merge$1({}, tokens, darkTokens);
const AtlantisThemeContext = createContext(atlantisThemeContextDefaultValues);
function AtlantisThemeContextProvider({ children, dangerouslyOverrideTheme, dangerouslyOverrideTokens, storageKey, }) {
    if (dangerouslyOverrideTheme) {
        return (React__default.createElement(InternalStaticThemeProvider, { dangerouslyOverrideTokens: dangerouslyOverrideTokens, dangerouslyOverrideTheme: dangerouslyOverrideTheme }, children));
    }
    return (React__default.createElement(InternalDynamicThemeProvider, { dangerouslyOverrideTokens: dangerouslyOverrideTokens, storageKey: storageKey }, children));
}
function InternalDynamicThemeProvider({ children, dangerouslyOverrideTokens, storageKey, }) {
    const theme = useSyncExternalStore(themeStore.subscribe, themeStore.getSnapshot, themeStore.getServerSnapshot);
    const { finalTokens, cssVariableOverrides } = useTokens(theme, dangerouslyOverrideTokens);
    useEffect(() => {
        if (!storageKey || !globalThis.window)
            return;
        function handleStorage(event) {
            if (event.key !== storageKey)
                return;
            const next = event.newValue;
            if (next !== "light" && next !== "dark")
                return;
            if (next === themeStore.getSnapshot())
                return;
            updateTheme(next);
        }
        globalThis.window.addEventListener("storage", handleStorage);
        return () => {
            globalThis.window.removeEventListener("storage", handleStorage);
        };
    }, [storageKey]);
    return (React__default.createElement(AtlantisThemeContext.Provider, { value: {
            theme: theme,
            tokens: finalTokens,
            overrideTokens: dangerouslyOverrideTokens,
        } }, cssVariableOverrides ? (React__default.createElement("div", { className: styles.overrideTokensWrapper, style: cssVariableOverrides }, children)) : (children)));
}
function InternalStaticThemeProvider({ dangerouslyOverrideTheme, children, dangerouslyOverrideTokens, }) {
    const { finalTokens, cssVariableOverrides } = useTokens(dangerouslyOverrideTheme, dangerouslyOverrideTokens);
    return (React__default.createElement(AtlantisThemeContext.Provider, { value: {
            theme: dangerouslyOverrideTheme,
            tokens: finalTokens,
            overrideTokens: dangerouslyOverrideTokens,
        } },
        React__default.createElement("div", { "data-theme": dangerouslyOverrideTheme, className: styles.staticThemeProviderWrapper, style: cssVariableOverrides }, children)));
}
function useAtlantisTheme() {
    return useContext(AtlantisThemeContext);
}
function getCssVariableOverrides(overrideTokens) {
    const cssVariables = Object.entries(overrideTokens).reduce((variables, [tokenName, tokenValue]) => {
        // @ts-expect-error - css variables are valid keys for style objects. @types/react may be outdated.
        variables[`--${tokenName}`] = tokenValue;
        return variables;
    }, {});
    return cssVariables;
}
function useTokens(theme, overrideTokens) {
    const currentTokens = theme === "dark" ? actualDarkTokens : tokens;
    const finalTokens = useMemo(() => {
        if (overrideTokens) {
            return merge$1({}, currentTokens, overrideTokens);
        }
        return currentTokens;
    }, [currentTokens, overrideTokens]);
    const cssVariableOverrides = overrideTokens
        ? getCssVariableOverrides(overrideTokens)
        : undefined;
    return {
        finalTokens,
        cssVariableOverrides,
    };
}

export { AtlantisThemeContextProvider as A, THEME_CHANGE_EVENT as T, atlantisThemeContextDefaultValues as a, updateTheme as b, useAtlantisTheme as u };
