import { Dictionary } from "@insite/client-framework/Common/Types";
import { getCurrentPage } from "@insite/client-framework/Store/Data/Pages/PageSelectors";
import PermissionsModel from "@insite/client-framework/Types/PermissionsModel";
import { HasConfirmationContext, withConfirmation } from "@insite/shell/Components/Modals/ConfirmationContext";
import {
    canAddChildPage,
    canAddVariant,
    canCopyPage,
    canDeleteLayout,
    canDeletePage,
    canEditLayout,
    canEditPage,
} from "@insite/shell/Components/PageTree/PageTreeFlyout.Functions";
import { getPageDefinition } from "@insite/shell/DefinitionLoader";
import { getPublishedPageVersions } from "@insite/shell/Services/ContentAdminService";
import { ShellThemeProps } from "@insite/shell/ShellTheme";
import { showErrorModal } from "@insite/shell/Store/ErrorModal/ErrorModalActionCreator";
import { editPageOptions } from "@insite/shell/Store/PageEditor/PageEditorActionCreators";
import {
    deletePage,
    openAddPage,
    openCopyPage,
    openCopyVariant,
    openCreateVariant,
    openMakeDefaultVariant,
    openReorderPages,
    openRulesEdit,
} from "@insite/shell/Store/PageTree/PageTreeActionCreators";
import { TreeNodeModel } from "@insite/shell/Store/PageTree/PageTreeState";
import ShellState from "@insite/shell/Store/ShellState";
import { TS } from "@insite/shell/TS";
import * as React from "react";
import { connect, ResolveThunks } from "react-redux";
import { RouteComponentProps, withRouter } from "react-router";
import styled from "styled-components";

interface OwnProps {
    flyOutNode: TreeNodeModel;
    flyOutElement: HTMLElement;
    closeFlyOut: () => void;
    nodesByParentId: Dictionary<TreeNodeModel[]>;
}

const mapStateToProps = (state: ShellState) => ({
    currentPageId: getCurrentPage(state).id,
    permissions: state.shellContext.permissions,
    mobileCmsModeActive: state.shellContext.mobileCmsModeActive,
    futurePublishNodeIds: state.pageTree.futurePublishNodeIds,
});

const mapDispatchToProps = {
    deletePage,
    openAddPage,
    openCopyPage,
    editPageOptions,
    openCopyVariant,
    openCreateVariant,
    openMakeDefaultVariant,
    openRulesEdit,
    openReorderPages,
    showErrorModal,
};

type Props = RouteComponentProps &
    ReturnType<typeof mapStateToProps> &
    ResolveThunks<typeof mapDispatchToProps> &
    OwnProps &
    HasConfirmationContext;

const flyOutOption = (onClick: () => void, title: string) => {
    return (
        <FlyoutOption onClick={onClick} data-test-selector={`pageFlyOutOption_${title}`}>
            {title}
        </FlyoutOption>
    );
};

class PageTreeFlyOut extends React.Component<Props> {
    private handleDeletePage = () => {
        this.props.closeFlyOut();

        const childNodes = this.props.nodesByParentId[this.props.flyOutNode.nodeId];
        const messageDetails = this.props.flyOutNode.isRootVariant
            ? TS("All variant pages other than the default will be removed.")
            : childNodes
            ? TS("This will immediately delete the page and its {0} children.", childNodes.length.toString())
            : TS("This will take effect immediately.");

        const name =
            (this.props.flyOutNode.variantName || this.props.flyOutNode.displayName) +
            (this.props.flyOutNode.isRootVariant ? ` ${TS("Variants")}` : "");
        this.props.confirmation.display({
            message: TS(
                "Are you sure you want to delete {0}? {1}",
                name,
                this.props.flyOutNode.isVariant ? "" : messageDetails,
            ),
            title: TS("Delete {0}", name),
            onConfirm: () => {
                this.props.deletePage(
                    this.props.flyOutNode.nodeId,
                    this.props.history,
                    this.props.flyOutNode.isVariant || this.props.flyOutNode.isRootVariant
                        ? this.props.flyOutNode.pageId
                        : "",
                );
            },
        });
    };

    private handleAddPage = () => {
        this.props.closeFlyOut();
        this.props.openAddPage(this.props.flyOutNode.nodeId, this.props.flyOutNode.type);
    };

    private handleCopyPage = () => {
        this.props.closeFlyOut();
        this.props.openCopyPage(
            this.props.flyOutNode.parentId,
            this.props.flyOutNode.pageId,
            `${this.props.flyOutNode.displayName}Copy`,
            this.props.flyOutNode.type,
        );
    };

    private handleReorderVariants = () => {
        this.props.closeFlyOut();
        this.props.openReorderPages(this.props.flyOutNode.nodeId);
    };

    private handleEditPage = () => {
        this.props.closeFlyOut();

        this.props.editPageOptions(this.props.flyOutNode.pageId, !!this.props.flyOutNode.isVariant);
    };

    private handleCreateVariant = async () => {
        this.props.closeFlyOut();
        if (!this.props.flyOutNode.isRootVariant) {
            const publishedVersions = await getPublishedPageVersions(this.props.flyOutNode.pageId, 1, 1);
            if (publishedVersions.totalItemCount === 0) {
                this.props.showErrorModal(
                    TS("Page should be published before creating any variant."),
                    undefined,
                    undefined,
                    TS("Create Variant"),
                );
                return;
            }
        }
        this.props.openCreateVariant(
            this.props.flyOutNode.parentId,
            this.props.flyOutNode.pageId,
            this.props.flyOutNode.displayName,
            this.props.flyOutNode.type,
        );
    };

    private handleCopyVariant = () => {
        this.props.closeFlyOut();
        this.props.openCopyVariant(
            this.props.flyOutNode.parentId,
            this.props.flyOutNode.pageId,
            this.props.flyOutNode.displayName,
            this.props.flyOutNode.type,
        );
    };

    private handleEditRules = () => {
        this.props.closeFlyOut();
        this.props.openRulesEdit(this.props.flyOutNode.pageId);
    };

    private handleMakeDefault = async () => {
        this.props.closeFlyOut();
        const publishedVersions = await getPublishedPageVersions(this.props.flyOutNode.pageId, 1, 1);
        if (publishedVersions.totalItemCount === 0) {
            this.props.showErrorModal(
                TS(
                    "This variant cannot be made the default because it has not been published. Publish the variant and try again.",
                ),
                undefined,
                undefined,
                TS("Make Default"),
            );
            return;
        }
        this.props.openMakeDefaultVariant(this.props.flyOutNode.parentId, this.props.flyOutNode.pageId);
    };

    render() {
        const { flyOutNode, permissions, mobileCmsModeActive } = this.props;
        const style = this.getFlyOutStyle();
        const pageDefinition = getPageDefinition(flyOutNode.type);
        const isLayout = flyOutNode.type === "Layout";
        const variantCount =
            this.props.nodesByParentId[this.props.flyOutNode.nodeId]?.filter(o => o.isVariant && !o.isDefaultVariant)
                .length || 0;

        if (isLayout) {
            return (
                <PageTreeFlyOutMenu style={style}>
                    {permissions && canEditLayout(permissions) && flyOutOption(this.handleEditPage, TS("Edit Layout"))}
                    {permissions &&
                        canDeleteLayout(permissions) &&
                        flyOutOption(this.handleDeletePage, TS("Delete Layout"))}
                </PageTreeFlyOutMenu>
            );
        }

        if (!permissions) {
            return null;
        }

        return (
            <PageTreeFlyOutMenu style={style}>
                {canEditPage(this.props.futurePublishNodeIds, pageDefinition, permissions, flyOutNode) &&
                    flyOutOption(
                        this.handleEditPage,
                        flyOutNode.isRootVariant
                            ? TS("Edit Shared Fields")
                            : !flyOutNode.isVariant
                            ? TS("Edit Page")
                            : TS("Edit Variant"),
                    )}
                {!mobileCmsModeActive && (
                    <>
                        {canAddChildPage(pageDefinition, permissions, flyOutNode) &&
                            flyOutOption(this.handleAddPage, TS("Add Page"))}
                    </>
                )}
                {canAddVariant(pageDefinition, permissions) &&
                    !flyOutNode.isVariant &&
                    flyOutOption(this.handleCreateVariant, TS("Create Variant"))}
                {canAddVariant(pageDefinition, permissions) &&
                    flyOutNode.isVariant &&
                    !flyOutNode.isDefaultVariant &&
                    flyOutOption(this.handleEditRules, TS("Edit Rules"))}
                {canAddVariant(pageDefinition, permissions) &&
                    flyOutNode.isVariant &&
                    !flyOutNode.isDefaultVariant &&
                    flyOutOption(this.handleMakeDefault, TS("Make Default"))}
                {canCopyPage(pageDefinition, permissions, flyOutNode) &&
                    flyOutOption(this.handleCopyPage, TS("Copy Page"))}
                {flyOutNode.isRootVariant &&
                    variantCount > 1 &&
                    flyOutOption(this.handleReorderVariants, TS("Reorder Variants"))}
                {canAddVariant(pageDefinition, permissions) &&
                    flyOutNode.isVariant &&
                    flyOutOption(this.handleCopyVariant, TS("Copy Variant"))}
                {canDeletePage(this.props.futurePublishNodeIds, pageDefinition, permissions, flyOutNode) &&
                    flyOutOption(
                        this.handleDeletePage,
                        flyOutNode.isRootVariant ? TS("Delete Variants") : TS("Delete"),
                    )}
            </PageTreeFlyOutMenu>
        );
    }

    private getFlyOutStyle() {
        let left = 10;
        let top = 0;
        if (this.props.flyOutElement) {
            const rect = this.props.flyOutElement.getBoundingClientRect();
            left = rect.right;
            top = rect.top;
        }

        if (top + 200 > window.innerHeight) {
            return {
                bottom: window.innerHeight - top - 40,
                left,
            };
        }

        return {
            left,
            top,
        };
    }
}

export const pageTreeFlyOutMenuHasItems = (
    futurePublishNodeIds: Dictionary<Date>,
    flyOutNode: TreeNodeModel,
    permissions: PermissionsModel | undefined,
): boolean => {
    const pageDefinition = getPageDefinition(flyOutNode.type);

    return (
        !!permissions &&
        (canEditPage(futurePublishNodeIds, pageDefinition, permissions, flyOutNode) ||
            canAddChildPage(pageDefinition, permissions, flyOutNode) ||
            canAddVariant(pageDefinition, permissions) ||
            canCopyPage(pageDefinition, permissions, flyOutNode) ||
            canDeletePage(futurePublishNodeIds, pageDefinition, permissions, flyOutNode) ||
            (flyOutNode.type === "Layout" && (canEditLayout(permissions) || canDeleteLayout(permissions))))
    );
};

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(withConfirmation(PageTreeFlyOut)));

const PageTreeFlyOutMenu = styled.div`
    border-radius: 8px;
    background-color: ${(props: ShellThemeProps) => props.theme.colors.common.accent};
    box-shadow: 0 2px 11px 0 rgba(0, 0, 0, 0.2);
    position: fixed;
    display: block;
    z-index: 1000;
`;

const FlyoutOption = styled.div`
    font-family: ${(props: ShellThemeProps) => props.theme.typography.body.fontFamily};
    color: ${(props: ShellThemeProps) => props.theme.colors.text.main};
    cursor: pointer;
    width: 180px;
    padding: 3px 0 3px 10px;
    font-weight: 300;
    display: flex;
    align-content: center;
    position: relative;
    align-items: center;

    &:hover {
        background-color: ${(props: ShellThemeProps) => props.theme.colors.common.backgroundContrast};
        color: ${(props: ShellThemeProps) => props.theme.colors.common.background};
    }

    &:first-child {
        border-top-left-radius: 8px;
        border-top-right-radius: 8px;
    }

    &:last-child {
        border-bottom-left-radius: 8px;
        border-bottom-right-radius: 8px;
    }
`;
