import { BaseTheme } from "@insite/mobius/globals/baseTheme";
import breakpointMediaQueries, { BreakpointKey, breakpointKeys } from "@insite/mobius/utilities/breakpointMediaQueries";
import { StyledProp } from "@insite/mobius/utilities/InjectableCss";
import injectCss from "@insite/mobius/utilities/injectCss";
import MobiusStyledComponentProps from "@insite/mobius/utilities/MobiusStyledComponentProps";
import React, { FC, useEffect, useState } from "react";
import styled, { css, ThemeProps, withTheme } from "styled-components";

export type HiddenProps = MobiusStyledComponentProps<
    "div",
    {
        /** Breakpoint name above which the component will be hidden */
        above?: BreakpointKey;
        /** The DOM element to render. */
        as?: keyof JSX.IntrinsicElements;
        /** Breakpoint name below which the component will be hidden */
        below?: BreakpointKey;
        /** CSS string or styled-components function to be injected into this component. */
        css?: StyledProp<HiddenProps>;
        /** When true, hidden children will not be rendered */
        doNotRenderIfHidden?: boolean;
    }
>;

const breakpointKeyValues = {
    [breakpointKeys[0]]: 0,
    [breakpointKeys[1]]: 1,
    [breakpointKeys[2]]: 2,
    [breakpointKeys[3]]: 3,
    [breakpointKeys[4]]: 4,
};

const HiddenStyle = styled.div.attrs<
    { as: keyof JSX.IntrinsicElements },
    { above?: BreakpointKey; as: any; below?: BreakpointKey }
>(props => ({
    as: props.as,
}))`
    max-width: 100%;
    max-height: 100%;
    ${({ theme, below }) => {
        const rules = Array(5);
        if (below) {
            rules[breakpointKeyValues[below] - 1] = css`
                display: none;
            `;
            return breakpointMediaQueries(theme, rules, "max");
        }
        return null;
    }}
    ${({ theme, above }) => {
        const rules = Array(5);
        if (above) {
            rules[breakpointKeyValues[above] + 1] = css`
                display: none;
            `;
            return breakpointMediaQueries(theme, rules, "min");
        }
        return null;
    }}
    ${injectCss}
`;

/**
 * This responsive component wrapper hides the enclosed content above, below and in
 * between given breakpoints based on theme breakpoint values.
 */
const Hidden: FC<HiddenProps & ThemeProps<BaseTheme>> = props => {
    const [windowResizeTime, setWindowResizeTime] = useState(0);

    useEffect(() => {
        const onWindowResize = () => {
            setWindowResizeTime(Date.now());
        };

        window.addEventListener("resize", onWindowResize);
        onWindowResize();

        return () => {
            window.removeEventListener("resize", onWindowResize);
        };
    }, []);

    if (props.doNotRenderIfHidden && !IS_SERVER_SIDE) {
        if (props.below && window.innerWidth < props.theme.breakpoints.values[breakpointKeyValues[props.below]]) {
            return null;
        }

        if (props.above && window.innerWidth >= props.theme.breakpoints.values[breakpointKeyValues[props.above] + 1]) {
            return null;
        }
    }

    return <HiddenStyle {...props} windowResizeTime={windowResizeTime} />;
};

/** @component */
export default withTheme(Hidden);
