import LoadingSpinner from "@insite/mobius/LoadingSpinner";
import applyPropBuilder from "@insite/mobius/utilities/applyPropBuilder";
import DisablerContext from "@insite/mobius/utilities/DisablerContext";
import getProp from "@insite/mobius/utilities/getProp";
import { StyledProp } from "@insite/mobius/utilities/InjectableCss";
import injectCss from "@insite/mobius/utilities/injectCss";
import MobiusStyledComponentProps from "@insite/mobius/utilities/MobiusStyledComponentProps";
import safeColor from "@insite/mobius/utilities/safeColor";
import VisuallyHidden from "@insite/mobius/VisuallyHidden";
import * as React from "react";
import styled, { withTheme } from "styled-components";

interface LoadingOverlayPresentationProps {
    /** CSS string or styled-components function to be injected into this component.
     * @themable */
    css?: StyledProp<LoadingOverlayProps>;
    /**
     * Indicates how the `css` property is combined with the variant `css` property from the theme.
     * If true, the variant css is applied first and then the component css is applied after causing
     * a merge, much like normal CSS. If false, only the component css is applied, overriding the variant css in the theme.
     */
    mergeCss?: boolean;
}

export type LoadingOverlayProps = MobiusStyledComponentProps<
    "div",
    LoadingOverlayPresentationProps & {
        /** CSS string or styled-components function to be injected into this component. */
        /** Show/hide status of the backdrop and spinner. */
        loading?: boolean;
        /** Props to be passed to the inner LoadingSpinner component. */
        spinnerProps?: any;
    }
>;

const LoadingOverlayStyle = styled.div<Pick<LoadingOverlayProps, "css">>`
    display: inline-block;
    position: relative;
    width: 100%;
    & > div:first-child {
        height: 100%;
        width: 100%;
        background: ${props => safeColor(props.theme.colors.common.background).rgb().alpha(0.6).string()};
        will-change: opacity;
        opacity: 0;
        transition: opacity 0.2s ease-in-out;
        ${injectCss}
        &[data-loading="true"] {
            opacity: 1;
            position: absolute;
            z-index: ${getProp("theme.zIndex.loadingOverlay")};
        }
        &[data-loading="false"] {
            position: fixed;
            display: none;
        }
    }
`;

const SpinnerWrapper = styled.div`
    position: absolute;
    top: 50%;
    left: 50%;
    right: 50%;
    bottom: 50%;
    margin: auto;
`;

/**
 * Adds a layer over the content to indicate that it is loading.
 */
const LoadingOverlay: React.FC<LoadingOverlayProps> = withTheme(props => {
    const { children, mergeCss, loading, spinnerProps, theme, ...otherProps } = props;
    const { applyStyledProp } = applyPropBuilder(props, { component: "loadingOverlay" });
    const resolvedMergeCss = mergeCss ?? otherProps?.theme?.loadingOverlay?.defaultProps?.mergeCss;

    return (
        <LoadingOverlayStyle css={applyStyledProp("css", resolvedMergeCss)} {...otherProps}>
            <div data-loading={loading} data-test-selector="loadingOverlaySpinner" role="alert" aria-live="assertive">
                <VisuallyHidden>
                    {loading ? theme.translate("loading content") : theme.translate("content loaded")}
                </VisuallyHidden>
                <SpinnerWrapper>
                    <LoadingSpinner {...spinnerProps} />
                </SpinnerWrapper>
            </div>
            <DisablerContext.Provider value={{ disable: loading }}>{children}</DisablerContext.Provider>
        </LoadingOverlayStyle>
    );
});

LoadingOverlay.defaultProps = {
    loading: true,
};

/** @component */
export default LoadingOverlay;

export { LoadingOverlayStyle };
