import React from 'react';
import { useState } from 'react';
import { Dialog, showDialog } from '@jupyterlab/apputils';
import TreeView from '@mui/lab/TreeView';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import TreeItem from '@mui/lab/TreeItem';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import databaseSvgStr from '../style/icons/database-solid-ghost.svg';
import {
  fetchTables,
  // IDatasource,
  IDatasourceItem,
  setDatasource,
  useDatasourceList
} from './api/datasource';
import { DatasourceFormDialogBody } from './DatasourceForm';

const dragImg = new Image();
dragImg.src = 'data:image/svg+xml;base64,' + btoa(databaseSvgStr);

interface IProps {
  manager: string;
  onOpenTable: (
    datasource: IDatasourceItem,
    tableName: string,
    action: 'schema' | 'content'
  ) => void;
}

const DatasourceView: React.FunctionComponent<IProps> = ({
  manager,
  onOpenTable
}) => {
  const { datasources, loading, error, refresh } = useDatasourceList(manager);
  const [tables, setTables] = useState<
    Partial<{
      [key: string]: string[] | 'error';
    }>
  >({});

  const [contextMenu, setContextMenu] = React.useState<{
    mouseX: number;
    mouseY: number;
  } | null>(null);

  const [contextData, setContextData] = React.useState<{
    datasource: IDatasourceItem;
    tableName: string;
  }>({} as any);

  const handleContextMenu = (
    event: React.MouseEvent,
    datasource: IDatasourceItem,
    tableName: string
  ) => {
    event.preventDefault();
    setContextMenu(
      contextMenu === null
        ? {
            mouseX: event.clientX + 2,
            mouseY: event.clientY - 6
          }
        : // repeated contextmenu when it is already open closes it with Chrome 84 on Ubuntu
          // Other native context menus might behave different.
          // With this behavior we prevent contextmenu from the backdrop to re-locale existing context menus.
          null
    );
    setContextData({ datasource, tableName });
  };

  const handleClose = () => {
    setContextMenu(null);
  };

  const handleContent = () => {
    setContextMenu(null);
    onOpenTable(contextData.datasource, contextData.tableName, 'content');
  };

  const handleSchema = () => {
    setContextMenu(null);
    onOpenTable(contextData.datasource, contextData.tableName, 'schema');
  };

  const contextMenuView = (
    <Menu
      open={contextMenu !== null}
      onClose={handleClose}
      anchorReference="anchorPosition"
      anchorPosition={
        contextMenu !== null
          ? { top: contextMenu.mouseY, left: contextMenu.mouseX }
          : undefined
      }
    >
      <MenuItem onClick={handleContent}>预览表内容</MenuItem>
      <MenuItem onClick={handleSchema}>预览表结构</MenuItem>
    </Menu>
  );

  const handleNodeToggle = async (id: number) => {
    if (!tables[id]) {
      const tables = await fetchTables(id);
      setTables(prev => {
        return { [id]: tables, ...prev };
      });
    }
  };

  let tree: JSX.Element[] | JSX.Element;
  if (error) {
    tree = <div style={{ padding: '0px 12px' }}>Error</div>;
  } else if (loading) {
    tree = <div style={{ padding: '0px 12px' }}>Loading ...</div>;
  } else {
    tree = datasources!.map(ds => {
      const table = tables[ds.id];
      let tableItems;
      if (!table) {
        tableItems = (
          <TreeItem nodeId={`${ds.id}-loading`} label="Loading ..." />
        );
      } else if (table === 'error') {
        tableItems = <TreeItem nodeId={`${ds.id}-error`} label="Error" />;
      } else if (table.length === 0) {
        tableItems = <TreeItem nodeId={`${ds.id}-error`} label="Empty" />;
      } else {
        tableItems = table.map(t => {
          return (
            <TreeItem
              title={t}
              key={`${ds.id}-${t}`}
              nodeId={`${ds.id}-${t}`}
              label={t}
              onDoubleClick={() => onOpenTable(ds, t, 'content')}
              onContextMenu={evt => handleContextMenu(evt, ds, t)}
              draggable="true"
              onDragStart={evt => {
                const data = {
                  operation: 'addToCanvas',
                  data: {
                    editType: 'createNode',
                    finalized: true,
                    nodeTemplate: {
                      id: `${ds.manager}-${ds.id}-${t}`,
                      op: `database-${ds.datasource}-input`,
                      description: t,
                      type: 'execution_node',
                      label: t,
                      inputs: [],
                      parameters: {},
                      outputs: [
                        {
                          id: 'outPort',
                          app_data: {
                            ui_data: {
                              cardinality: {
                                min: 0,
                                max: -1
                              },
                              label: 'Output Port'
                            }
                          }
                        }
                      ],
                      app_data: {
                        ui_data: {
                          description: t,
                          image: '',
                          label: t
                        }
                      }
                    }
                  }
                };
                evt.dataTransfer.setData('text/plain', JSON.stringify(data));
                evt.dataTransfer.setDragImage(dragImg, 10, 10);
              }}
              onFocusCapture={e => e.stopPropagation()}
            />
          );
        });
      }
      return (
        <TreeItem
          key={ds.id}
          nodeId={ds.id.toString()}
          label={ds.database_name}
          onClick={() => handleNodeToggle(ds.id)}
        >
          {tableItems}
          {contextMenuView}
        </TreeItem>
      );
    });
  }

  const handleAddDatasource = async () => {
    const { value } = await showDialog({
      title: '添加数据源',
      body: new DatasourceFormDialogBody(manager),
      hasClose: true,
      buttons: [
        Dialog.okButton({ label: '确定' }),
        Dialog.cancelButton({ label: '取消' })
      ]
    });
    if (!value) {
      return;
    }
    await setDatasource(value);
    refresh();
  };

  return (
    <div style={{ flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
      <div
        style={{ display: 'flex', justifyContent: 'flex-end', padding: '10px' }}
      >
        <button className="jldbq-btn-add" onClick={handleAddDatasource}>
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="20"
            height="20"
            fill="currentColor"
            viewBox="0 0 16 16"
          >
            <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
          </svg>
          添加数据源
        </button>
      </div>
      <TreeView
        aria-label="file system navigator"
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        sx={{ overflow: 'auto', flexGrow: 1 }}
      >
        {tree}
      </TreeView>
    </div>
  );
};

export default DatasourceView;
