import React, { useCallback, useEffect, useState, useMemo } from "react";
import ReactDOM from "react-dom";
import { ButtonGroup, Button, Form } from "react-bootstrap";
import styled from "styled-components";
import DataGrid, { Cell, editors } from "react-data-grid";
import { lighten } from "polished";
import { filter } from "lodash";

import { useStoreState, useStoreActions } from "appstore";
import Utils from "utils";

const HEADER_INCOMPLETE_VALUE = "<Identify Column>";

const confidenceHeatmapRange = Utils.getConfidenceScoreHeatmapRange();

const getHeatmapIndex = (confidence: number) => {
  const range = Utils.getConfidenceScoreHeatmapRange();
  for (var i = 0; i < range.length; i++) {
    const { score } = range[i];
    if (confidence < score) {
      return i;
    }
  }
  return range.length - 1;
};

const TableViewContainer = styled.div`
  width: 50%;
  overflow: auto;
  margin: 1rem 0.5rem;
  display: flex;
  align-items: center;
  flex-direction: column;

  padding: 1rem 1.25rem;
  background: white;
  border-radius: 8px;
  box-shadow: 0 0px 4.2px rgba(0, 0, 0, 0.2), 0 6.7px 6.3px rgba(0, 0, 0, 0.158);
`;

const TableHeaderContainer = styled.div`
  display: flex;
  flex-direction: column;
  width: 100%;
`;

const TableHeaderTitle = styled.div`
  font-size: 16px;
  font-weight: 500;
  margin-bottom: 0.5rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
`;

const TableHeaderSubtitle = styled.div`
  font-size: 14px;
  font-weight: 700;
  margin-bottom: 0.5rem;
  color: ${(p) => p.theme.gray600};
  display: flex;
  justify-content: space-between;
  align-items: center;
  select {
    min-width: 200px;
  }
`;

const ConfidenceFilterContainer = styled.div`
  display: flex;
  flex-direction: row;
  align-self: start;
  font-size: 14px;
  padding: 0.5rem 0;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  font-weight: 300;
  border-bottom: 1px solid ${(p) => p.theme.gray100};
  margin-bottom: 0.5rem;
  .btn-group {
    flex-wrap: wrap;
    .btn {
      flex: 0;
      white-space: nowrap;
      display: flex;
      font-size: 12px;
      align-items: center;
      border-left: 1px solid ${(p) => p.theme.gray150};
      &:first-child {
        border-left: none;
      }
    }
  }
`;

const Square = styled.div`
  display: inline-block;
  height: 16px;
  width: 16px;
  margin-left: 0.5rem;
  background: ${(p) => p.color};
  word-wrap: none;
`;

const TableHeader = () => {
  const selectedPageNumber = useStoreState(
    (s) => s.internal.selectedPageNumber
  );
  const selectedTableIndex = useStoreState(
    (s) => s.internal.selectedTableIndex
  );
  const documentPage = useStoreState((s) =>
    s.documentModel.documentPages.find(
      (documentPage) => documentPage.pageNumber === selectedPageNumber
    )
  );
  const selectedTable =
    documentPage && documentPage.tables.length > 0
      ? documentPage.tables[selectedTableIndex]
      : undefined;
  const tableTypes = useStoreState((s) => s.documentModel.tableTypes);
  const selectedTableType =
    selectedTable && selectedTable.tableType
      ? tableTypes.find((t) => t.name === selectedTable.tableType)
      : undefined;
  const selectedTableTypeColumnTypes = selectedTableType?.columnTypes;

  // Total number of columns
  const numColumns =
    selectedTable && selectedTable.rows.length > 0
      ? selectedTable.rows[0].length
      : 0;
  // Number of columns the user has assigned values to
  const numAssignedColumns = filter(
    Object.values(selectedTable?.headerColumnTypes || []),
    (o) => o
  ).length;

  const numPages = useStoreState((s) => s.documentModel.numPages);
  const documentPages = useStoreState((s) => s.documentModel.documentPages);
  const selectedConfidenceScoreIndex = useStoreState(
    (s) => s.internal.selectedConfidenceScoreIndex
  );
  const setSelectedConfidenceScoreIndex = useStoreActions(
    (s) => s.setSelectedConfidenceScoreIndex
  );
  const setSelectedPageNumber = useStoreActions((s) => s.setSelectedPageNumber);
  const setSelectedTableIndex = useStoreActions((s) => s.setSelectedTableIndex);
  const setSelectedTableTableType = useStoreActions(
    (s) => s.setSelectedTableTableType
  );
  const setNewPageFromTable = useStoreActions((s) => s.setNewPageFromTable);

  if (documentPages.length === 0) {
    return <div className="text-muted">Loading...</div>;
  }

  const onSelectConfidenceScore = (index: number) => {
    if (selectedConfidenceScoreIndex === index) {
      setSelectedConfidenceScoreIndex({
        selectedConfidenceScoreIndex: undefined,
      });
      return;
    }
    setSelectedConfidenceScoreIndex({
      selectedConfidenceScoreIndex: index,
    });
  };

  const prevPage = () => {
    setSelectedPageNumber({ pageNumber: selectedPageNumber - 1 });
    setNewPageFromTable(selectedPageNumber - 1);
  };
  const nextPage = () => {
    setSelectedPageNumber({ pageNumber: selectedPageNumber + 1 });
    setNewPageFromTable(selectedPageNumber + 1);
  };
  const prevTable = () => {
    setSelectedTableIndex({ tableIndex: selectedTableIndex - 1 });
  };
  const nextTable = () => {
    setSelectedTableIndex({ tableIndex: selectedTableIndex + 1 });
  };
  const selectTableType = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value || null;
    setSelectedTableTableType(value);
  };

  return (
    <TableHeaderContainer>
      <TableHeaderTitle>
        <div>
          Annotating Page {selectedPageNumber} of {numPages}
        </div>
        <div>
          <Button
            size="sm"
            variant="primary"
            className="mr-1"
            onClick={prevPage}
            disabled={selectedPageNumber === 1}
          >
            Previous Page
          </Button>
          <Button
            size="sm"
            variant="primary"
            onClick={nextPage}
            disabled={selectedPageNumber === numPages}
          >
            Next Page
          </Button>
        </div>
      </TableHeaderTitle>
      <TableHeaderSubtitle>
        {documentPage && selectedTable ? (
          <>
            <div>
              Table {selectedTableIndex + 1} of {documentPage.tables.length}
            </div>
            <div>
              <Button
                size="sm"
                variant="light"
                className="mr-1"
                onClick={prevTable}
                disabled={selectedTableIndex === 0}
              >
                Previous Table
              </Button>
              <Button
                size="sm"
                variant="light"
                onClick={nextTable}
                disabled={selectedTableIndex + 1 === documentPage.tables.length}
              >
                Next Table
              </Button>
            </div>
          </>
        ) : (
          <div className="text-success">
            No tables found on page {selectedPageNumber}
          </div>
        )}
      </TableHeaderSubtitle>
      <TableHeaderSubtitle>
        {documentPage && selectedTable ? (
          <>
            <div>Identify Table Type</div>
            <div>
              <Form.Control
                as="select"
                size="sm"
                value={selectedTable.tableType || ""}
                onChange={selectTableType}
              >
                <option value="">{"<Select Table Type>"}</option>
                {tableTypes.map((tableType, tableTypeIndex) => (
                  <option key={tableTypeIndex} value={tableType.name}>
                    {tableType.name}
                  </option>
                ))}
              </Form.Control>
            </div>
          </>
        ) : null}
      </TableHeaderSubtitle>

      {selectedTableType && !selectedTableTypeColumnTypes ? (
        <TableHeaderSubtitle>
          <div className="text-success">
            No column selection necessary for table type{" "}
            {`"${selectedTableType.name}"`}
          </div>
        </TableHeaderSubtitle>
      ) : null}

      {selectedTableType &&
      selectedTableTypeColumnTypes &&
      numAssignedColumns < numColumns ? (
        <TableHeaderSubtitle>
          <div className="text-warning">
            Please select {numColumns - numAssignedColumns} column types for
            {`"${selectedTableType.name}"`} table
          </div>
        </TableHeaderSubtitle>
      ) : null}

      {selectedTableType &&
      selectedTableTypeColumnTypes &&
      numAssignedColumns === numColumns ? (
        <TableHeaderSubtitle>
          <div className="text-success">
            Assigned {numAssignedColumns} / {numColumns} column types for{" "}
            {`"${selectedTableType.name}"`} table
          </div>
        </TableHeaderSubtitle>
      ) : null}

      <ConfidenceFilterContainer>
        <div className="mr-3 d-flex align-items-center ">Confidence Score</div>
        <ButtonGroup>
          {confidenceHeatmapRange.map(({ score, color }, index) => (
            <Button
              key={index}
              value={index}
              size="sm"
              variant="light"
              active={index === selectedConfidenceScoreIndex}
              onClick={() => onSelectConfidenceScore(index)}
            >
              {index === 0 ? "<" : ">"} {score * 100}% <Square color={color} />
            </Button>
          ))}
        </ButtonGroup>
      </ConfidenceFilterContainer>
    </TableHeaderContainer>
  );
};

const TableGridContainer = styled.div`
  flex: 1;
  width: 100%;
  overflow: auto;
  .react-grid-Grid {
    border: 0px;
  }
  .custom-cell-content {
    padding: 4px;
  }

  .react-grid-Main {
    height: 100%;
  }

  .react-grid-Container {
    height: 100%;
  }

  .react-grid-Grid {
    min-height: 100% !important;
  }

  .react-grid-Canvas {
    height: 100% !important;
  }
  .react-grid-HeaderCell {
    background: ${(p) => lighten(0.1, p.theme.gray100)};
    border-right: 1px dotted ${(p) => p.theme.gray100};
  }
  .react-grid-Cell {
    padding: 0;
    padding-left: 0px;
    padding-right: 0px;
    line-height: 2;
    border-right: 0px;
    border-top: 1px solid ${(p) => p.theme.gray100};
  }
  .cell-container.active {
    .react-grid-Cell__value {
      border: 4px solid ${(p) => "#ffff76"};
    }
    .custom-cell-content {
      padding: 0;
    }
  }

  .cell-container.header-complete {
    .react-grid-Cell__value {
      border: 1px solid ${(p) => p.theme.gray300};
      background: ${(p) => p.theme.gray200};
    }
    .custom-cell-content {
    }
  }
  .cell-container.header-incomplete {
    .react-grid-Cell__value {
      border: 1px solid ${(p) => lighten(0.35, p.theme.yellow)};
      background: ${(p) => lighten(0.3, p.theme.yellow)};
    }
    .custom-cell-content {
    }
  }
  .cell-container.header-disabled {
    .react-grid-Cell__value {
      border: 1px solid ${(p) => p.theme.gray300};
      background: ${(p) => p.theme.gray300};
      color: ${(p) => p.theme.gray600};
      font-style: italic;
    }
    .custom-cell-content {
      cursor: not-allowed;
    }
  }
`;
interface CellValue {
  value: string | number;
  selected: boolean;
  confidence: number;
  isColumnSelector: boolean;
  columnTypes?: string[];
}
interface RowData {
  [columnName: string]: CellValue;
}

interface RowDataUpdate {
  [columnName: string]: string;
}

type OptionType =
  | string
  | {
      id: string;
      value: string;
      title: string;
      text?: string;
    };
type CustomEditorProps = {
  options?: OptionType[];
};

interface ExcelColumn {
  editable: boolean;
  name: any;
  key: string;
  width: number;
  resizeable: boolean;
  filterable: boolean;
}

interface EditorBaseProps {
  value: any;
  column: ExcelColumn;
  height: number;
  onBlur: () => void;
  onCommit: () => void;
  onCommitCancel: () => void;
  rowData: any;
  rowMetaData: any;
}

class CustomTextAndSelectEditor extends editors.EditorBase<
  CustomEditorProps,
  {}
> {
  getInputNode() {
    const inputNode = super.getInputNode();
    if (!inputNode) {
      return ReactDOM.findDOMNode(this);
    }
    return inputNode;
  }

  render() {
    const value = this.props.value as CellValue;
    if (value.isColumnSelector) {
      return this.renderSelectEditor();
    } else {
      return this.renderTextEditor();
    }
  }

  renderTextEditor() {
    return (
      <input
        type="text"
        onBlur={this.props.onBlur}
        className="form-control"
        defaultValue={this.props.value.value}
      />
    );
  }

  onChange() {}

  renderSelectEditor() {
    const value = this.props.value as CellValue;
    if (!value.columnTypes) {
      return (
        <select style={this.getStyle()} disabled={true}>
          <option value="">{HEADER_INCOMPLETE_VALUE}</option>
        </select>
      );
    }
    return (
      <select
        style={this.getStyle()}
        defaultValue={value.value}
        onBlur={this.props.onBlur}
        onChange={this.onChange}
      >
        <option value="">{HEADER_INCOMPLETE_VALUE}</option>
        {value.columnTypes.map((name, index) => (
          <option key={index} value={name}>
            {name}
          </option>
        ))}
      </select>
    );
  }
}

class CustomCell extends React.Component<{
  value: CellValue;
}> {
  render() {
    const { value } = this.props;
    if (!value) {
      return null;
    }
    const classNames = ["cell-container"];
    if (value.selected) {
      classNames.push("active");
    }

    if (value.isColumnSelector && value.value) {
      classNames.push("header-complete");
    }
    if (value.isColumnSelector && !value.columnTypes) {
      classNames.push("header-disabled");
    } else if (
      value.isColumnSelector &&
      value.value === HEADER_INCOMPLETE_VALUE
    ) {
      classNames.push("header-incomplete");
    }
    return (
      <div className={classNames.join(" ")}>
        <Cell {...this.props} />
      </div>
    );
  }
}

const TableGrid = () => {
  const [rowData, setRowData] = useState<RowData[]>([]);

  const selectedPageNumber = useStoreState(
    (s) => s.internal.selectedPageNumber
  );
  const selectedTableIndex = useStoreState(
    (s) => s.internal.selectedTableIndex
  );
  const tableTypes = useStoreState((s) => s.documentModel.tableTypes);
  const documentPages = useStoreState((s) => s.documentModel.documentPages);
  const selectedPage = documentPages.find(
    (documentPage) => documentPage.pageNumber === selectedPageNumber
  );
  const selectedTable =
    selectedPage && selectedPage.tables.length > 0
      ? selectedPage.tables[selectedTableIndex]
      : undefined;

  const selectedTableType =
    selectedTable && selectedTable.tableType
      ? tableTypes.find((t) => t.name === selectedTable.tableType)
      : undefined;
  const selectedTableTypeColumnTypes = selectedTableType?.columnTypes;
  const selectedConfidenceScoreIndex = useStoreState(
    (s) => s.internal.selectedConfidenceScoreIndex
  );
  const numColumns =
    selectedTable && selectedTable.rows.length > 0
      ? selectedTable.rows[0].length
      : 0;
  const updateTableCell = useStoreActions((s) => s.updateTableCell);
  const setTableColumnType = useStoreActions((s) => s.setTableColumnType);

  const setSelectedTag = useStoreActions((s) => s.setSelectedTag);

  const confidenceHeatmapRange = Utils.getConfidenceScoreHeatmapRange();

  // Translate HIL Input document table rows into react-data-grid rows
  useEffect(() => {
    if (!selectedTable) {
      return;
    }
    const localRows: RowData[] = [];
    // Add header row for selecting column type
    const localRow: RowData = {};
    Array.from({ length: numColumns }, (x, i) => i).forEach((columnIndex) => {
      const columnIndexStr = `${columnIndex}`;
      const value = selectedTable.headerColumnTypes[columnIndexStr]
        ? selectedTable.headerColumnTypes[columnIndexStr]
        : HEADER_INCOMPLETE_VALUE;

      localRow[columnIndexStr] = {
        value: value,
        selected: false,
        confidence: 100,
        isColumnSelector: true,
        columnTypes: selectedTableTypeColumnTypes,
      };
    });
    localRows.push(localRow);

    // Add all other rows
    selectedTable.rows.forEach((row, rowIndex) => {
      const localRow: RowData = {};
      row.forEach((cell, columnIndex) => {
        // The textract pipeline outputs empty cells with 0% confidence.
        // Change to 100% confidence to not overload the heatmap with red
        // for empty cells
        const confidence = cell.text === "" ? 100 : cell.confidence;
        const heatmapIndex = getHeatmapIndex(confidence / 100);
        localRow[`${columnIndex}`] = {
          value: cell.editedText !== undefined ? cell.editedText : cell.text,
          selected: heatmapIndex === selectedConfidenceScoreIndex,
          confidence: confidence,
          isColumnSelector: false,
        };
      });
      localRows.push(localRow);
    });

    setRowData(localRows);
  }, [
    selectedTable,
    selectedPageNumber,
    selectedConfidenceScoreIndex,
    selectedTableTypeColumnTypes,
    numColumns,
  ]);

  const formatter = useCallback(
    ({ value }: { value: CellValue }) => {
      const heatmapIndex = getHeatmapIndex(value.confidence / 100);
      const style: React.CSSProperties = {};
      let title = `${value.value || "\u00A0"}\n\nConfidence: ${Math.floor(
        value.confidence
      )}%`;
      if (!value.isColumnSelector) {
        style.background = `${confidenceHeatmapRange[heatmapIndex].color}`;
      }
      if (value.isColumnSelector && !value.columnTypes) {
        title = "Identify table type before identifying columns";
      } else if (value.isColumnSelector) {
        title = "Identify column type";
      }
      return (
        <div className="custom-cell-content" title={title} style={style}>
          {value.value || "\u00A0"}
        </div>
      );
    },
    [confidenceHeatmapRange]
  );

  const dataGridColumns = useMemo(() => {
    return Array.from({ length: numColumns }, (x, i) => i).map(
      (index: number) => ({
        key: `${index}`,
        name: `Column ${index + 1}`,
        formatter: formatter,
        editor: CustomTextAndSelectEditor,
        resizable: true,
      })
    );
  }, [numColumns, formatter]);

  if (selectedTable === undefined) {
    return null;
  }

  const column1Actions = [
    {
      icon: <span className="glyphicon glyphicon-remove" />,
      callback: () => {
        alert("Locate");
      },
    },
    {
      icon: "glyphicon glyphicon-link",
      actions: [
        {
          text: "Option 1",
          callback: () => {
            alert("Option 1 clicked");
          },
        },
        {
          text: "Option 2",
          callback: () => {
            alert("Option 2 clicked");
          },
        },
      ],
    },
  ];

  function getCellActions(column: DataGrid.Column<any>, row: any) {
    const cellActions = new Map();
    cellActions.set("1", column1Actions);

    console.log(`${cellActions}`);

    return column1Actions;
  }

  const handleCellSelect = (e: { rowIdx: number; idx: number }) => {
    if (e.rowIdx >= 1) {
      setSelectedTag(selectedTable.rows[e.rowIdx - 1][e.idx]);
    }
  };

  const handleCellDeselect = (e: { rowIdx: number; idx: number }) => {
    setSelectedTag({ tag: undefined });
  };

  return (
    <TableGridContainer className="ag-theme-alpine">
      <DataGrid
        columns={dataGridColumns}
        rowGetter={(i) => rowData[i]}
        rowsCount={rowData.length}
        minHeight={1000}
        // getCellActions={getCellActions}
        rowRenderer={({ renderBaseRow, ...props }) => {
          return renderBaseRow({ ...props, cellRenderer: CustomCell });
        }}
        onGridRowsUpdated={({
          fromRow,
          toRow,
          action,
          updated,
          ...otherProps
        }) => {
          const updatedValues = updated as {
            [columnName: string]: string;
          };
          // Only handle cell updates (no cell drags)
          if (action.toLowerCase().indexOf("update") === -1) {
            return;
          }
          // @ts-ignore - react-data-grid typings are outdated
          const updatedRowData = otherProps.fromRowData as RowData;
          for (let i = fromRow; i < toRow + 1; i++) {
            Object.keys(updatedValues).forEach((columnName) => {
              const cell = updatedRowData[columnName];
              const updatedValue = updatedValues[columnName];
              const columnIndex = dataGridColumns.findIndex(
                ({ key }) => key === columnName
              );
              if (columnIndex === -1) {
                return;
              }

              if (cell.isColumnSelector) {
                setTableColumnType({
                  columnIndex,
                  columnType: updatedValue,
                });
              } else {
                // If there is a column selection row, offset the row index by 1
                // const rowIndex = selectedTableTypeColumnTypes ? i - 1 : i;
                const rowIndex = i - 1;
                updateTableCell({
                  pageNumber: selectedPageNumber,
                  table: selectedTableIndex,
                  row: rowIndex,
                  column: columnIndex,
                  value: `${updatedValue}`,
                });
              }
            });
          }
        }}
        enableCellSelect={true}
        onCellSelected={(e) => {
          handleCellSelect(e);
        }}
        onCellDeSelected={(e) => {
          handleCellDeselect(e);
        }}
      />
    </TableGridContainer>
  );
};

export default () => {
  return (
    <TableViewContainer>
      <TableHeader />
      <TableGrid />
    </TableViewContainer>
  );
};