import {
  ContextualMenu,
  DetailsList,
  DetailsListLayoutMode,
  DetailsRow,
  FocusZone,
  IButtonProps,
  IColumn,
  IconButton,
  IContextualMenuProps,
  IDetailsListProps,
  IDetailsRowProps,
  IObjectWithKey,
  ISelectionZoneProps,
  ITextField,
  ITextFieldProps,
  Selection,
  SelectionMode,
  SelectionZone,
  TextField,
} from "@fluentui/react";
import * as React from "react";
import * as _ from "underscore";
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
import * as Constants from "../../../Common/Constants";
import { StyleConstants } from "../../../Common/Constants";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
import { QueriesClient } from "../../../Common/QueriesClient";
import * as DataModels from "../../../Contracts/DataModels";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { useDialog } from "../Dialog";

const title = "Open Saved Queries";

export interface QueriesGridComponentProps {
  queriesClient: QueriesClient;
  onQuerySelect: (query: DataModels.Query) => void;
  containerVisible: boolean;
  saveQueryEnabled: boolean;
}

export interface QueriesGridComponentState {
  queries: Query[];
  filteredResults: Query[];
}

interface Query extends DataModels.Query, IObjectWithKey {
  key: string;
}

export class QueriesGridComponent extends React.Component<QueriesGridComponentProps, QueriesGridComponentState> {
  private selection: Selection;
  private queryFilter: ITextField;

  constructor(props: QueriesGridComponentProps) {
    super(props);
    this.state = {
      queries: [],
      filteredResults: [],
    };
    this.selection = new Selection();
    this.selection.setItems(this.state.filteredResults);
  }

  public componentDidUpdate(prevProps: QueriesGridComponentProps, prevState: QueriesGridComponentState): void {
    this.selection.setItems(
      this.state.filteredResults,
      !_.isEqual(prevState.filteredResults, this.state.filteredResults)
    );
    this.queryFilter && this.queryFilter.focus();
    const querySetupCompleted: boolean = !prevProps.saveQueryEnabled && this.props.saveQueryEnabled;
    const noQueryFiltersApplied: boolean = !this.queryFilter || !this.queryFilter.value;
    if (!this.props.containerVisible || !this.props.saveQueryEnabled) {
      return;
    } else if (noQueryFiltersApplied && (!prevProps.containerVisible || querySetupCompleted)) {
      // refresh only when pane is opened or query setup was recently completed
      this.fetchSavedQueries();
    }
  }

  // fetched saved queries when panel open
  public componentDidMount() {
    this.fetchSavedQueries();
  }

  public render(): JSX.Element {
    if (this.state.queries.length === 0) {
      return this.renderBannerComponent();
    }
    return this.renderQueryGridComponent();
  }

  private renderQueryGridComponent(): JSX.Element {
    const searchFilterProps: ITextFieldProps = {
      placeholder: "Search for Queries",
      ariaLabel: "Query filter input",
      onChange: this.onFilterInputChange,
      componentRef: (queryInput: ITextField) => (this.queryFilter = queryInput),
      styles: {
        root: { paddingBottom: "12px" },
        field: { fontSize: `${StyleConstants.mediumFontSize}px` },
      },
    };
    const selectionContainerProps: ISelectionZoneProps = {
      selection: this.selection,
      selectionMode: SelectionMode.single,
      onItemInvoked: (item: Query) => this.props.onQuerySelect(item),
    };
    const detailsListProps: IDetailsListProps = {
      items: this.state.filteredResults,
      columns: this.getColumns(),
      isHeaderVisible: false,
      setKey: "queryName",
      layoutMode: DetailsListLayoutMode.fixedColumns,
      selection: this.selection,
      selectionMode: SelectionMode.none,
      compact: true,
      onRenderRow: this.onRenderRow,
      styles: {
        root: { width: "100%" },
      },
    };

    return (
      <FocusZone style={{ width: "100%" }}>
        <TextField {...searchFilterProps} />
        <SelectionZone {...selectionContainerProps}>
          <DetailsList {...detailsListProps} />
        </SelectionZone>
      </FocusZone>
    );
  }

  private renderBannerComponent(): JSX.Element {
    const bannerProps: React.ImgHTMLAttributes<HTMLImageElement> = {
      src: SaveQueryBannerIcon,
      alt: "Save query helper banner",
      style: {
        height: "150px",
        width: "310px",
        marginTop: "20px",
        border: `1px solid ${StyleConstants.BaseMedium}`,
      },
    };
    return (
      <div id="emptyQueryBanner">
        <div>
          You have not saved any queries yet. <br /> <br />
          To write a new query, open a new query tab and enter the desired query. Once ready to save, click on Save
          Query and follow the prompt in order to save the query.
        </div>
        <img {...bannerProps} />
      </div>
    );
  }

  private onFilterInputChange = (event: React.FormEvent<HTMLInputElement>, query: string): void => {
    if (query) {
      const filteredQueries: Query[] = this.state.queries.filter(
        (savedQuery: Query) =>
          savedQuery.queryName.indexOf(query) > -1 || savedQuery.queryName.toLowerCase().indexOf(query) > -1
      );
      this.setState({
        filteredResults: filteredQueries,
      });
    } else {
      // no filter
      this.setState({
        filteredResults: this.state.queries,
      });
    }
  };

  private onRenderRow = (props: IDetailsRowProps): JSX.Element => {
    props.styles = {
      root: { width: "100%" },
      fields: {
        width: "100%",
        justifyContent: "space-between",
      },
      cell: {
        margin: "auto 0",
      },
    };
    return <DetailsRow data-selection-invoke={true} {...props} />;
  };

  private getColumns(): IColumn[] {
    return [
      {
        key: "Name",
        name: "Name",
        fieldName: "queryName",
        minWidth: 260,
      },
      {
        key: "Action",
        name: "Action",
        fieldName: undefined,
        minWidth: 70,
        onRender: (query: Query) => {
          const buttonProps: IButtonProps = {
            iconProps: {
              iconName: "More",
              title: "More",
              ariaLabel: "More actions button",
            },
            menuIconProps: {
              styles: { root: { display: "none" } },
            },
            menuProps: {
              isBeakVisible: true,
              items: [
                {
                  key: "Open",
                  text: "Open query",
                  onClick: () => {
                    this.props.onQuerySelect(query);
                  },
                },
                {
                  key: "Delete",
                  text: "Delete query",
                  onClick: async () => {
                    useDialog.getState().showOkCancelModalDialog(
                      "Confirm delete",
                      "Are you sure you want to delete this query?",
                      "Delete",
                      async () => {
                        const startKey: number = TelemetryProcessor.traceStart(Action.DeleteSavedQuery, {
                          dataExplorerArea: Constants.Areas.ContextualPane,
                          paneTitle: title,
                        });
                        try {
                          await this.props.queriesClient.deleteQuery(query);
                          TelemetryProcessor.traceSuccess(
                            Action.DeleteSavedQuery,
                            {
                              dataExplorerArea: Constants.Areas.ContextualPane,
                              paneTitle: title,
                            },
                            startKey
                          );
                        } catch (error) {
                          TelemetryProcessor.traceFailure(
                            Action.DeleteSavedQuery,
                            {
                              dataExplorerArea: Constants.Areas.ContextualPane,
                              paneTitle: title,
                              error: getErrorMessage(error),
                              errorStack: getErrorStack(error),
                            },
                            startKey
                          );
                        }
                        await this.fetchSavedQueries(); // get latest state
                      },
                      "Cancel",
                      undefined
                    );
                  },
                },
              ],
            },
            menuAs: (menuProps: IContextualMenuProps): JSX.Element => {
              return <ContextualMenu {...menuProps} />;
            },
          };
          return <IconButton {...buttonProps} />;
        },
      },
    ];
  }

  private async fetchSavedQueries(): Promise<void> {
    let queries: Query[];
    try {
      queries = (await this.props.queriesClient.getQueries()) as Query[];
    } catch (error) {
      console.error(error);
      return;
    }
    queries = queries.map((query: Query) => {
      query.key = query.queryName;
      return query;
    });

    // we do a deep equality check before setting the state to avoid infinite re-renders
    if (!_.isEqual(queries, this.state.queries)) {
      this.setState({
        filteredResults: queries,
        queries: queries,
      });
    }
  }
}