/*
 * Copyright 2016 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { mount } from "enzyme";
import * as sinon from "sinon";

import { Classes as CoreClasses, H4, Menu, MenuItem } from "@blueprintjs/core";
import { beforeEach, describe, expect, it } from "@blueprintjs/test-commons/vitest";

import { ColumnHeaderCell, type ColumnHeaderCellProps } from "..";
import * as Classes from "../common/classes";
import { ElementHarness } from "../harness";
import { createTableOfSize } from "../mocks/table";

describe("<ColumnHeaderCell>", () => {
    it("Default renderer", () => {
        const { container } = render(createTableOfSize(3, 2));
        const table = new ElementHarness(container);
        const text = table.find(`.${Classes.TABLE_COLUMN_NAME_TEXT}`, 1).text();
        expect(text).to.equal("B");
    });

    it("renders with custom className if provided", () => {
        const CLASS_NAME = "my-custom-class-name";
        const { container } = render(<ColumnHeaderCell className={CLASS_NAME} />);
        const table = new ElementHarness(container);
        const hasCustomClass = table.find(`.${Classes.TABLE_HEADER}`, 0).hasClass(CLASS_NAME);
        expect(hasCustomClass).to.be.true;
    });

    it("passes index prop to nameRenderer callback if index was provided", () => {
        const renderNameStub = sinon.stub();
        renderNameStub.returns(<span>name</span>);
        const NAME = "my-name";
        const INDEX = 17;
        mount(<ColumnHeaderCell index={INDEX} name={NAME} nameRenderer={renderNameStub} />);
        expect(renderNameStub.firstCall.args).to.deep.equal([NAME, INDEX]);
    });

    describe("Custom renderer", () => {
        const menuClickSpy = sinon.spy();

        beforeEach(() => {
            menuClickSpy.resetHistory();
        });

        it("renders custom name", () => {
            const columnHeaderCellRenderer = (columnIndex: number) => {
                return <ColumnHeaderCell name={`COLUMN-${columnIndex}`} />;
            };
            const { container } = render(createTableOfSize(3, 2, { columnHeaderCellRenderer }));
            const table = new ElementHarness(container);
            const text = table.find(`.${Classes.TABLE_COLUMN_NAME_TEXT}`, 1).text();
            expect(text).to.equal("COLUMN-1");
        });

        it("renders custom content", () => {
            const columnHeaderCellRenderer = (columnIndex: number) => {
                return (
                    <ColumnHeaderCell name={`COLUMN-${columnIndex}`}>
                        <H4>Header of {columnIndex}</H4>
                    </ColumnHeaderCell>
                );
            };
            const { container } = render(createTableOfSize(3, 2, { columnHeaderCellRenderer }));
            const table = new ElementHarness(container);
            const text = table.find(`.${Classes.TABLE_HEADER_CONTENT} h4`, 2).text();
            expect(text).to.equal("Header of 2");
        });

        it("renders custom menu items with a menuRenderer callback", async () => {
            const columnHeaderCellRenderer = (columnIndex: number) => (
                <ColumnHeaderCell name={`COL-${columnIndex}`} menuRenderer={renderMenu} />
            );
            const { container } = render(createTableOfSize(3, 2, { columnHeaderCellRenderer }));
            const table = new ElementHarness(container);

            await expectMenuToOpen(table);

            // attempt to click one of the menu items
            ElementHarness.document().find('[data-icon="export"]').mouse("click");

            expect(menuClickSpy.called, "expected menu item click handler to be called").to.be.true;
        });

        it("custom menu supports popover props", async () => {
            const expectedMenuPopoverProps = {
                placement: "right-start" as const,
                popoverClassName: "test-popover-class",
            };
            const columnHeaderCellRenderer = (columnIndex: number) => (
                <ColumnHeaderCell
                    name={`COL-${columnIndex}`}
                    menuRenderer={renderMenu}
                    menuPopoverProps={expectedMenuPopoverProps}
                />
            );
            const { container } = render(createTableOfSize(3, 2, { columnHeaderCellRenderer }));
            const table = new ElementHarness(container);

            await expectMenuToOpen(table);

            const popover = ElementHarness.document().find(`.${CoreClasses.POPOVER}`);
            expect(
                popover.hasClass(expectedMenuPopoverProps.popoverClassName),
                `expected popover element to have ${expectedMenuPopoverProps.popoverClassName} class`,
            ).to.be.true;
            expect(
                popover.hasClass(`${CoreClasses.POPOVER_CONTENT_PLACEMENT}-right`),
                `expected popover element to have '${expectedMenuPopoverProps.placement}' placement classes applied`,
            ).to.be.true;
        });

        it("renders loading state properly", () => {
            const columnHeaderCellRenderer = (columnIndex: number) => {
                return <ColumnHeaderCell loading={columnIndex === 0} name="Column Header" />;
            };
            const { container } = render(createTableOfSize(2, 1, { columnHeaderCellRenderer }));
            const table = new ElementHarness(container);
            expect(table.find(`.${Classes.TABLE_COLUMN_HEADERS} .${Classes.TABLE_HEADER}`, 0).text()).to.equal("");
            expect(table.find(`.${Classes.TABLE_COLUMN_HEADERS} .${Classes.TABLE_HEADER}`, 1).text()).to.equal(
                "Column Header",
            );
        });

        function renderMenu() {
            return (
                <Menu>
                    <MenuItem icon="export" onClick={menuClickSpy} text="Teleport" />
                    <MenuItem icon="sort-alphabetical-desc" onClick={menuClickSpy} text="Down with ZA!" />
                    <MenuItem icon="curved-range-chart" onClick={menuClickSpy} text="Psi" />
                </Menu>
            );
        }

        async function expectMenuToOpen(table: ElementHarness) {
            const target = table.find(`.${Classes.TABLE_TH_MENU}.${CoreClasses.POPOVER_TARGET}`);
            await userEvent.click(target.element!);
            expect(
                target.hasClass(CoreClasses.POPOVER_OPEN),
                "expected th menu popover target element to have 'popover open' indicator class",
            ).to.be.true;
        }
    });

    describe("Reorder handle", () => {
        const REORDER_HANDLE_CLASS = Classes.TABLE_REORDER_HANDLE_TARGET;

        it("shows reorder handle in interaction bar if reordering and interaction bar are enabled", () => {
            const wrapper = mountHeaderCell();
            expect(wrapper.find(`.${Classes.TABLE_INTERACTION_BAR} .${REORDER_HANDLE_CLASS}`).exists()).to.be.true;
        });

        it("shows reorder handle next to column name if reordering enabled but interaction bar disabled", () => {
            const wrapper = mountHeaderCell({ enableColumnInteractionBar: false });
            expect(wrapper.find(`.${Classes.TABLE_COLUMN_NAME} .${REORDER_HANDLE_CLASS}`).exists()).to.be.true;
        });

        function mountHeaderCell(props?: Partial<ColumnHeaderCellProps>) {
            return mount(
                <ColumnHeaderCell
                    enableColumnInteractionBar={true}
                    reorderHandle={<div className={REORDER_HANDLE_CLASS} />}
                    enableColumnReordering={true}
                    {...props}
                />,
            );
        }
    });
});
