/*
 * <<
 * Davinci
 * ==
 * Copyright (C) 2016 - 2017 EDP
 * ==
 * 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 React from 'react'
import { compose, Dispatch } from 'redux'
import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'
import memoizeOne from 'memoize-one'
import Helmet from 'react-helmet'
import { Link } from 'react-router-dom'
import { RouteComponentWithParams } from 'utils/types'

import injectReducer from 'utils/injectReducer'
import injectSaga from 'utils/injectSaga'
import reducer from './reducer'
import sagas from './sagas'

import { checkNameUniqueAction } from 'containers/App/actions'
import { ViewActions, ViewActionType } from './actions'
import { makeSelectLoading, makeSelectViews } from './selectors'
import { makeSelectCurrentProject } from 'containers/Projects/selectors'

import ModulePermission from '../Account/components/checkModulePermission'
import { initializePermission } from '../Account/components/checkUtilPermission'

import {
  Breadcrumb,
  Button,
  Col,
  Dropdown,
  Icon,
  Menu,
  message,
  Popconfirm,
  Row,
  Spin,
  Table,
  Tooltip,
  Tree
} from 'antd'
import { ColumnProps, PaginationConfig, SorterResult } from 'antd/lib/table'
import { ButtonProps } from 'antd/lib/button'
import Container, { ContainerBody, ContainerTitle } from 'components/Container'
import Box from 'components/Box'
import SearchFilterDropdown from 'components/SearchFilterDropdown'
import CopyModal from './components/CopyModal'
import CatalogueModal from './components/CatalogueModal'

import { ICatalogue, IViewBase, IViewLoading } from './types'
import { IProject } from '../Projects/types'

import utilStyles from 'assets/less/util.less'
import styles from './index.less'
import request from 'utils/request'
import api from 'utils/api'
import classnames from 'classnames'

interface IViewListStateProps {
  views: IViewBase[]
  currentProject: IProject
  loading: IViewLoading
}

interface IViewListDispatchProps {
  onLoadViews: (projectId: number, parentId: number) => void
  onDeleteView: (viewId: number, resolve: () => void) => void
  onCopyView: (view: IViewBase, resolve: () => void) => void
  onCheckName: (data, resolve, reject) => void
}

type IViewListProps = IViewListStateProps &
  IViewListDispatchProps &
  RouteComponentWithParams

// tslint:disable-next-line:interface-name
interface Catalogue {
  description?: string // 资源描述
  extConfig?: string // 扩展信息
  id?: number
  industry?: string // 行业分类
  name?: string // 资源名称
  originDept?: string // 来源部门
  originSystem?: string // 来源系统
  parentId?: string
  projectId?: number
}

interface IViewListStates {
  screenWidth: number
  tempFilterViewName: string
  filterViewName: string
  filterDropdownVisible: boolean
  tableSorter: SorterResult<IViewBase>

  copyModalVisible: boolean
  copyFromView: IViewBase

  viewList: IViewBase[]

  catalogueModalVisible: boolean
  catalogueFromView: ICatalogue
  saveCatalogueLoading: boolean

  catalogues: ICatalogue[]
  selectedCatalogueKeys: string[]

  treeLoading: boolean
  tableLoading: boolean
}

const { TreeNode, DirectoryTree } = Tree

export class ViewList extends React.PureComponent<
  IViewListProps,
  IViewListStates
> {
  // @ts-ignore
  public state: Readonly<IViewListStates> = {
    screenWidth: document.documentElement.clientWidth,
    tempFilterViewName: '',
    filterViewName: '',
    filterDropdownVisible: false,
    tableSorter: null,

    copyModalVisible: false,
    copyFromView: null,

    catalogueModalVisible: false,
    catalogueFromView: null,
    saveCatalogueLoading: false,
    catalogues: [],

    viewList: [],

    treeLoading: false,
    tableLoading: false
  }

  public async componentWillMount() {
    const { projectId } = this.props.match.params

    await (projectId && this.getCatalogues())
    // const parentId = Number(this.state.selectedCatalogueKeys[0]) || null
    // tslint:disable-next-line:no-unused-expression
    projectId && this.loadViews()

    window.addEventListener('resize', this.setScreenWidth, false)
  }

  private loadViews = async () => {
    const { projectId } = this.props.match.params
    if (projectId && this.state.selectedCatalogueKeys.length > 0) {
      const parentId = Number(this.state.selectedCatalogueKeys[0])
      try {
        this.setState({ tableLoading: true })
        const data = await request(
          api.getViewsByParentId +
            `?projectId=${projectId}&parentId=${parentId}`,
          { method: 'get' }
        )
        this.setState({
          // @ts-ignore
          viewList: (data.payload as unknown as IViewBase[]) ?? []
        })
      } catch (e) {
        console.log(e)
      } finally {
        this.setState({ tableLoading: false })
      }
    }
  }

  public componentWillUnmount() {
    window.removeEventListener('resize', this.setScreenWidth, false)
  }

  private setScreenWidth = () => {
    this.setState({ screenWidth: document.documentElement.clientWidth })
  }

  private getFilterViews = memoizeOne(
    (viewName: string, views: IViewBase[]) => {
      if (!Array.isArray(views) || !views.length) {
        return []
      }
      const regex = new RegExp(viewName, 'gi')
      return views.filter(
        (v) => v.name.match(regex) || v.description.match(regex)
      )
    }
  )

  private static getViewPermission = memoizeOne((project: IProject) => ({
    viewPermission: initializePermission(project, 'viewPermission'),
    AdminButton: ModulePermission<ButtonProps>(project, 'view', true)(Button),
    EditButton: ModulePermission<ButtonProps>(project, 'view', false)(Button)
  }))

  private getTableColumns = ({
    viewPermission,
    AdminButton,
    EditButton
  }: ReturnType<typeof ViewList.getViewPermission>) => {
    // const { views } = this.props
    const { viewList } = this.state
    const {
      tempFilterViewName,
      filterViewName,
      filterDropdownVisible,
      tableSorter
    } = this.state
    const sourceNames = viewList.map(({ sourceName }) => sourceName)

    const columns: Array<ColumnProps<IViewBase>> = [
      {
        title: '名称',
        dataIndex: 'name',
        filterDropdown: (
          <SearchFilterDropdown
            placeholder="名称"
            value={tempFilterViewName}
            onChange={this.filterViewNameChange}
            onSearch={this.searchView}
          />
        ),
        filterDropdownVisible,
        onFilterDropdownVisibleChange: (visible: boolean) =>
          this.setState({ filterDropdownVisible: visible }),
        sorter: (a, b) => (a.name > b.name ? 1 : -1),
        sortOrder:
          tableSorter && tableSorter.columnKey === 'name'
            ? tableSorter.order
            : void 0
      },
      {
        title: '描述',
        dataIndex: 'description'
      },
      {
        title: '数据源',
        // title: 'Source',
        dataIndex: 'sourceName',
        filterMultiple: false,
        onFilter: (val, record) => record.sourceName === val,
        filters: sourceNames
          .filter((name, idx) => sourceNames.indexOf(name) === idx)
          .map((name) => ({ text: name, value: name }))
      }
    ]

    if (filterViewName) {
      const regex = new RegExp(`(${filterViewName})`, 'gi')
      columns[0].render = (text: string) => (
        <span
          dangerouslySetInnerHTML={{
            __html: text.replace(
              regex,
              `<span class='${utilStyles.highlight}'>$1</span>`
            )
          }}
        />
      )
    }

    if (viewPermission) {
      columns.push({
        title: '操作',
        width: 150,
        className: utilStyles.textAlignCenter,
        render: (_, record) => (
          <span className="ant-table-action-column">
            <Tooltip title="复制">
              <EditButton
                icon="copy"
                shape="circle"
                type="ghost"
                onClick={this.copyView(record)}
              />
            </Tooltip>
            <Tooltip title="修改">
              <EditButton
                icon="edit"
                shape="circle"
                type="ghost"
                onClick={this.editView(record.id)}
              />
            </Tooltip>
            <Popconfirm
              title="确定删除？"
              placement="bottom"
              onConfirm={this.deleteView(record.id)}
            >
              <Tooltip title="删除">
                <AdminButton icon="delete" shape="circle" type="ghost" />
              </Tooltip>
            </Popconfirm>
          </span>
        )
      })
    }

    return columns
  }

  private tableChange = (_1, _2, sorter: SorterResult<IViewBase>) => {
    this.setState({ tableSorter: sorter })
  }

  private filterViewNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({
      tempFilterViewName: e.target.value,
      filterViewName: ''
    })
  }

  private searchView = (value: string) => {
    this.setState({
      filterViewName: value,
      filterDropdownVisible: false
    })
    window.event.preventDefault()
  }

  private basePagination: PaginationConfig = {
    defaultPageSize: 20,
    showSizeChanger: true
  }

  private addView = () => {
    const { history, match } = this.props
    history.push(
      `/project/${
        match.params.projectId
      }/view?parentId=${this.state.selectedCatalogueKeys?.join()}`
    )
  }

  private copyView = (fromView: IViewBase) => () => {
    this.setState({
      copyModalVisible: true,
      copyFromView: fromView
    })
  }

  private copy = (view: IViewBase) => {
    const { onCopyView } = this.props
    onCopyView(view, () => {
      this.setState({
        copyModalVisible: false
      })
      message.info('数据资产 复制成功')
    })
  }

  private cancelCopy = () => {
    this.setState({ copyModalVisible: false })
  }

  private editView = (viewId: number) => () => {
    const { history, match } = this.props
    history.push(`/project/${match.params.projectId}/view/${viewId}`)
  }

  private deleteView = (viewId: number) => () => {
    const { onDeleteView } = this.props
    onDeleteView(viewId, () => {
      this.loadViews()
    })
  }

  private checkViewUniqueName = (
    viewName: string,
    resolve: () => void,
    reject: (err: string) => void
  ) => {
    const { currentProject, onCheckName } = this.props
    onCheckName(
      { name: viewName, projectId: currentProject.id },
      resolve,
      reject
    )
  }

  private getCatalogues = async () => {
    try {
      const { projectId } = this.props.match.params

      this.setState({ treeLoading: true })
      // @ts-ignore
      const { payload } = await request(
        api.getCatalogues + `?projectId=${projectId}`,
        { method: 'GET' }
      )
      this.setState({
        catalogues: payload as unknown as ICatalogue[],
        selectedCatalogueKeys: payload?.[0]?.id ? [`${payload?.[0]?.id}`] : []
      })
    } catch (e) {
      console.log()
    } finally {
      this.setState({ treeLoading: false })
    }
  }

  private handleSaveCatalogue = async (catalogue: ICatalogue) => {
    const { projectId } = this.props.match.params
    if (!projectId) {
      return
    }
    try {
      const parentId = Number(this.state.selectedCatalogueKeys[0]) || null
      const catalogueFromView = this.state.catalogueFromView
      this.setState({ saveCatalogueLoading: true })
      if (catalogueFromView) {
        await request(api.updateCatalogue + `/${catalogueFromView.id}`, {
          method: 'PUT',
          data: { ...catalogue, parentId, projectId }
      })
      } else {
        await request(api.createCatalogue, {
          method: 'post',
          data: { ...catalogue, parentId, projectId }
        })
      }
      this.setState({ catalogueModalVisible: false })
      this.getCatalogues()
    } finally {
      this.setState({
        saveCatalogueLoading: false,
        catalogueFromView: null
      })
    }
  }
  private handleEditCatalogue = (c: ICatalogue) => {
    this.setState({
      catalogueModalVisible: true,
      catalogueFromView: c
    })
  }

  private handleDeleteCatalogue = async (c: ICatalogue) => {
    try {
      this.setState({ treeLoading: true })
      const data = await request(api.deleteCatalogue + `/${c.id}`, {
        method: 'DELETE'
      })
      // @ts-ignore
      if (data?.header?.code === 200) {
        message.error({ content: '删除成功' })
        this.getCatalogues()
      } else {
        // @ts-ignore
        // tslint:disable-next-line:no-unused-expression
        data?.header?.msg && message.error({ content: data?.header?.msg })
      }
    } finally {
      this.setState({ treeLoading: false })
    }
  }

  private renderTree = (catalogues: ICatalogue[]) => {
    const { selectedCatalogueKeys } = this.state
    // tslint:disable-next-line:jsx-wrap-multiline
    return (
      <>
        {catalogues.map((c, idx) => (
          <React.Fragment key={c.id ?? idx}>
            <div
              key={c.id ?? idx}
              className={classnames(styles.treeNode, {
                [styles.treeNodeSelected]: selectedCatalogueKeys.includes(
                  `${c.id}`
                ),
                [styles.treeNodeChild]: !!c.parentId
              })}
            >
              <span
                className={styles.treeNodeLeft}
                onClick={() => {
                  this.setState({ selectedCatalogueKeys: [`${c.id}`] }, () => {
                    this.loadViews()
                  })
                }}
              >
                <Icon type="folder-open" />
                {c.name}
              </span>
              <Dropdown
                overlay={() => (
                  <Menu>
                    <Menu.Item
                      key="0"
                      onClick={() => this.handleEditCatalogue(c)}
                    >
                      编辑
                    </Menu.Item>
                    <Menu.Item key="1">
                      <Popconfirm
                        title="确定删除？"
                        placement="bottom"
                        onConfirm={() => this.handleDeleteCatalogue(c)}
                      >
                        <a> 删除</a>
                      </Popconfirm>
                    </Menu.Item>
                  </Menu>
                )}
                trigger={['click']}
              >
                <Icon type="more" />
              </Dropdown>
            </div>
            <div style={{ marginLeft: 20 }}>
              {c.children && this.renderTree(c.children)}
            </div>
          </React.Fragment>
        ))}
      </>
    )
  }

  public render() {
    const { currentProject, views, loading } = this.props
    const { screenWidth, filterViewName, viewList } = this.state
    const { viewPermission, AdminButton, EditButton } =
      ViewList.getViewPermission(currentProject)
    const tableColumns = this.getTableColumns({
      viewPermission,
      AdminButton,
      EditButton
    })
    const tablePagination: PaginationConfig = {
      ...this.basePagination,
      simple: screenWidth <= 768
    }
    const filterViews = this.getFilterViews(filterViewName, viewList)

    const {
      copyModalVisible,
      copyFromView,
      catalogueModalVisible,
      catalogueFromView
    } = this.state

    const pathname = this.props.history.location.pathname

    return (
      <>
        <Container>
          <Helmet title="数据资产" />
          {!pathname.includes('dataManager') && (
            <ContainerTitle>
              <Row>
                <Col span={24} className={utilStyles.shortcut}>
                  <Breadcrumb className={utilStyles.breadcrumb}>
                    <Breadcrumb.Item>
                      <Link to="">View</Link>
                    </Breadcrumb.Item>
                  </Breadcrumb>
                  <Link to={`/account/organization/${currentProject.orgId}`}>
                    <i className="iconfont icon-organization" />
                  </Link>
                </Col>
              </Row>
            </ContainerTitle>
          )}
          <ContainerBody>
            <Box>
              <Box.Header>
                <Box.Title>
                  <Icon type="bars" />
                  数据资产列表
                </Box.Title>
                <Box.Tools>
                  <Tooltip placement="bottom" title="新增">
                    <AdminButton
                      type="primary"
                      icon="plus"
                      onClick={this.addView}
                    />
                  </Tooltip>
                </Box.Tools>
              </Box.Header>
              <Box.Body>
                <div className={styles.treeTableContainer}>
                  <div className={styles.treeContainer}>
                    <div className={styles.treeTitle}>
                      <h6>资源目录列表</h6>
                      <div
                        className={styles.treePlusNode}
                        onClick={() => {
                          this.setState({ catalogueModalVisible: true })
                        }}
                      >
                        <Icon type="plus" />
                      </div>
                    </div>
                    <div className={styles.treeContent}>
                      <Spin spinning={this.state.treeLoading}>
                        {this.renderTree(this.state.catalogues)}
                      </Spin>
                    </div>
                  </div>
                  <Table
                    style={{ flex: 1 }}
                    bordered
                    rowKey="id"
                    loading={this.state.tableLoading}
                    dataSource={filterViews}
                    columns={tableColumns}
                    pagination={tablePagination}
                    onChange={this.tableChange}
                  />
                </div>
                <div style={{ padding: 20 }} />
              </Box.Body>
            </Box>
          </ContainerBody>
        </Container>
        <CopyModal
          visible={copyModalVisible}
          loading={loading.copy}
          fromView={copyFromView}
          onCheckUniqueName={this.checkViewUniqueName}
          onCopy={this.copy}
          onCancel={this.cancelCopy}
        />
        <CatalogueModal
          visible={catalogueModalVisible}
          loading={this.state.saveCatalogueLoading}
          fromView={catalogueFromView}
          onCheckUniqueName={this.checkViewUniqueName}
          onSave={this.handleSaveCatalogue}
          onCancel={() => this.setState({ catalogueModalVisible: false })}
        />
      </>
    )
  }
}

const mapDispatchToProps = (dispatch: Dispatch<ViewActionType>) => ({
  onLoadViews: (projectId, parentId) =>
    dispatch(ViewActions.loadViews(projectId, parentId)),
  onDeleteView: (viewId, resolve) =>
    dispatch(ViewActions.deleteView(viewId, resolve)),
  onCopyView: (view, resolve) => dispatch(ViewActions.copyView(view, resolve)),
  // @ts-ignore
  onCheckName: (data, resolve, reject) =>
    // @ts-ignore
    dispatch(checkNameUniqueAction('view', data, resolve, reject))
})

const mapStateToProps = createStructuredSelector({
  views: makeSelectViews(),
  currentProject: makeSelectCurrentProject(),
  loading: makeSelectLoading()
})

const withConnect = connect<
  IViewListStateProps,
  IViewListDispatchProps,
  RouteComponentWithParams
>(mapStateToProps, mapDispatchToProps)
const withReducer = injectReducer({ key: 'view', reducer })
const withSaga = injectSaga({ key: 'view', saga: sagas })

export default compose(withReducer, withSaga, withConnect)(ViewList)
