import {
  configureStore as configureReduxStore,
  combineReducers
} from "@reduxjs/toolkit";
import { createSelector } from "reselect";
import {
  createReplacementContext,
  replaceResourceIfNeeded,
  ContextResource
} from "@makeswift/controls";
import { copy as copyFromControl, merge } from "../controls/control";
import * as Documents from "./modules/read-only-documents";
import * as ElementTrees from "./modules/element-trees";
import * as ReactComponents from "./modules/react-components";
import * as ComponentsMeta from "./modules/components-meta";
import * as PropControllers from "./modules/prop-controllers";
import * as PropControllerHandles from "./modules/prop-controller-handles";
import * as IsInBuilder from "./modules/is-in-builder";
import * as IsPreview from "./modules/is-preview";
import * as BuilderEditMode from "./modules/builder-edit-mode";
import * as Breakpoints from "./modules/breakpoints";
import { ActionTypes } from "./actions";
import { changeElementTree, createElementTree, deleteElementTree } from "./actions/internal";
import { actionMiddleware, middlewareOptions, devToolsConfig } from "./toolkit";
import { withSetupTeardown } from "./mixins/setup-teardown";
import {
  createBaseDocument,
  createDocumentReference,
  getRootElement,
  isElementReference
} from "./modules/read-only-documents";
const reducer = combineReducers({
  documents: Documents.reducer,
  elementTrees: ElementTrees.reducer,
  reactComponents: ReactComponents.reducer,
  componentsMeta: ComponentsMeta.reducer,
  propControllers: PropControllers.reducer,
  propControllerHandles: PropControllerHandles.reducer,
  isInBuilder: IsInBuilder.reducer,
  isPreview: IsPreview.reducer,
  builderEditMode: BuilderEditMode.reducer,
  breakpoints: Breakpoints.reducer
});
function getDocumentsStateSlice(state) {
  return state.documents;
}
function getDocument(state, documentKey) {
  return Documents.getDocument(getDocumentsStateSlice(state), documentKey);
}
function getElementTreesSlice(state) {
  return state.elementTrees;
}
function getElements(state, documentKey) {
  return ElementTrees.getElements(getElementTreesSlice(state), documentKey);
}
function getElementIds(state, documentKey) {
  return ElementTrees.getElementIds(getElementTreesSlice(state), documentKey);
}
function getReactComponentsStateSlice(state) {
  return state.reactComponents;
}
function getReactComponent(state, type) {
  return ReactComponents.getReactComponent(getReactComponentsStateSlice(state), type);
}
function getComponentsMetaStateSlice(state) {
  return state.componentsMeta;
}
function getComponentMeta(state, type) {
  return ComponentsMeta.getComponentMeta(getComponentsMetaStateSlice(state), type);
}
function getPropControllersStateSlice(state) {
  return state.propControllers;
}
function getPropControllerDescriptors(state) {
  return PropControllers.getPropControllerDescriptors(getPropControllersStateSlice(state));
}
function getComponentPropControllerDescriptors(state, componentType) {
  return PropControllers.getComponentPropControllerDescriptors(
    getPropControllersStateSlice(state),
    componentType
  );
}
function getPropControllerHandlesStateSlice(state) {
  return state.propControllerHandles;
}
function getPropControllers(state, documentKey, elementKey) {
  return PropControllerHandles.getPropControllers(
    getPropControllerHandlesStateSlice(state),
    documentKey,
    elementKey
  );
}
const getDocumentKeysSortedByDepth = createSelector(
  [getDocumentsStateSlice, getElementTreesSlice],
  (documents, elementTrees) => {
    return [...documents.keys()].sort((a, b) => elementTrees.get(a)?.elements.has(b) ? -1 : 1);
  }
);
function getElement(state, documentKey, elementKey) {
  return ElementTrees.getElement(getElementTreesSlice(state), documentKey, elementKey);
}
function getElementId(state, documentKey, elementKey) {
  return ElementTrees.getElementId(getElementTreesSlice(state), documentKey, elementKey);
}
function getElementPropControllerDescriptors(state, documentKey, elementKey) {
  const element = getElement(state, documentKey, elementKey);
  if (element == null || Documents.isElementReference(element))
    return null;
  return getComponentPropControllerDescriptors(state, element.type);
}
function copyElementTree(state, elementTree, replacementContext) {
  function copyElementTreeNode(state2, replacementContext2) {
    return function(node) {
      const context = {
        replacementContext: replacementContext2,
        copyElement: copyElementTreeNode(state2, replacementContext2)
      };
      if (Documents.isElementReference(node)) {
        return {
          ...node,
          value: replaceResourceIfNeeded(ContextResource.GlobalElement, node.value, context)
        };
      }
      const descriptors = getComponentPropControllerDescriptors(state2, node.type);
      if (descriptors == null)
        return node;
      for (const [propKey, descriptor] of Object.entries(descriptors)) {
        node.props[propKey] = copyFromControl(descriptor, node.props[propKey], context);
      }
      return node;
    };
  }
  const copy = JSON.parse(JSON.stringify(elementTree));
  return copyElementTreeNode(state, createReplacementContext(replacementContext))(copy);
}
function mergeElement(state, baseElement, overrideElement) {
  if (baseElement.type !== overrideElement.type || baseElement.key !== overrideElement.key) {
    throw new Error(`Can't merge elements of different types or keys`);
  }
  if (Documents.isElementReference(overrideElement))
    return overrideElement;
  if (Documents.isElementReference(baseElement))
    return baseElement;
  const elementDescriptors = getPropControllerDescriptors(state);
  const descriptors = elementDescriptors.get(baseElement.type);
  if (descriptors == null) {
    throw new Error(
      `Can't merge element of type "${baseElement.type}" because it has no descriptors`
    );
  }
  const mergedProps = {};
  for (const propName of Object.keys(descriptors)) {
    const descriptor = descriptors[propName];
    const context = {
      mergeElement(base, override) {
        return mergeElement(state, base, override);
      }
    };
    mergedProps[propName] = merge(
      descriptor,
      baseElement.props[propName],
      overrideElement.props[propName],
      context
    );
  }
  return { ...baseElement, props: mergedProps };
}
function getIsInBuilder(state) {
  return state.isInBuilder;
}
function getIsPreview(state) {
  return IsPreview.getIsPreview(state.isPreview);
}
function getBuilderEditMode(state) {
  return state.builderEditMode;
}
function getBreakpoints(state) {
  return state.breakpoints;
}
function elementTreeMiddleware() {
  return actionMiddleware(({ dispatch, getState }) => (next) => {
    return (action) => {
      switch (action.type) {
        case ActionTypes.REGISTER_DOCUMENT:
          dispatch(
            createElementTree({
              document: action.payload.document,
              descriptors: getPropControllerDescriptors(getState())
            })
          );
          break;
        case ActionTypes.CHANGE_DOCUMENT: {
          const { documentKey, operation } = action.payload;
          const oldDocument = getDocument(getState(), documentKey);
          const result = next(action);
          const newDocument = getDocument(getState(), documentKey);
          if (oldDocument != null && newDocument != null && newDocument !== oldDocument) {
            dispatch(
              changeElementTree({
                oldDocument,
                newDocument,
                descriptors: getPropControllerDescriptors(getState()),
                operation
              })
            );
          }
          return result;
        }
        case ActionTypes.UNREGISTER_DOCUMENT:
          dispatch(deleteElementTree(action.payload));
          break;
      }
      return next(action);
    };
  });
}
function configureStore({
  name,
  preloadedState,
  breakpoints
}) {
  return configureReduxStore({
    reducer,
    preloadedState: {
      ...preloadedState,
      breakpoints: Breakpoints.getInitialState(breakpoints ?? preloadedState?.breakpoints)
    },
    middleware: (getDefaultMiddleware) => getDefaultMiddleware(middlewareOptions).concat(elementTreeMiddleware()),
    enhancers: (getDefaultEnhancers) => getDefaultEnhancers().concat(
      withSetupTeardown(
        () => {
        },
        () => {
        }
      )
    ),
    devTools: devToolsConfig({
      name: `${name} (${(/* @__PURE__ */ new Date()).toISOString()})`
    })
  });
}
export {
  configureStore,
  copyElementTree,
  createBaseDocument,
  createDocumentReference,
  elementTreeMiddleware,
  getBreakpoints,
  getBuilderEditMode,
  getComponentMeta,
  getComponentPropControllerDescriptors,
  getDocument,
  getDocumentKeysSortedByDepth,
  getElement,
  getElementId,
  getElementIds,
  getElementPropControllerDescriptors,
  getElements,
  getIsInBuilder,
  getIsPreview,
  getPropControllerDescriptors,
  getPropControllers,
  getReactComponent,
  getRootElement,
  isElementReference,
  mergeElement
};
//# sourceMappingURL=react-page.js.map