import FauxtonAPI from "../../core/api";

// 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 classnames from 'classnames';
import PropTypes from 'prop-types';
import React from "react";
import Components from "../components/react-components";
import ComponentsStore from "../components/stores";
import ComponentsActions from "../components/actions";
import PerPageSelector from '../documents/index-results/components/pagination/PerPageSelector';
import FauxtonComponentsReact from "..//fauxton/components";
import Stores from "./stores";
import Actions from "./actions";

import { Tooltip, OverlayTrigger, Button, Form, ButtonGroup } from 'react-bootstrap';

const databasesStore = Stores.databasesStore;
const deleteDbModalStore = ComponentsStore.deleteDbModalStore;

const {Accordion, AccordionItem, DeleteDatabaseModal,
  ToggleHeaderButton,
  TrayContents,
  ToolbarButton } = Components;


class DatabasesController extends React.Component {

  constructor(props) {
    super(props);
    this.reloadAfterDatabaseDeleted = this.reloadAfterDatabaseDeleted.bind(this);
  }

  getStoreState = () => {
    return {
      dbList: databasesStore.getDbList(),
      loading: databasesStore.isLoading(),
      showDeleteDatabaseModal: deleteDbModalStore.getShowDeleteDatabaseModal(),
      showPartitionedColumn: databasesStore.isPartitionedDatabasesAvailable(),
      page: databasesStore.getPage(),
      limit: databasesStore.getLimit()
    };
  };

  componentDidMount() {
    databasesStore.on('change', this.onChange, this);
    deleteDbModalStore.on('change', this.onChange, this);
    this.loadData();
  }

  componentDidUpdate(_, prevState) {
    if (prevState.page !== this.state.page || prevState.limit !== this.state.limit) {
      this.loadData();
    }
  }

  loadData() {
    Actions.init({
      page: this.state.page,
      limit: this.state.limit
    });
  }

  componentWillUnmount() {
    databasesStore.off('change', this.onChange, this);
    deleteDbModalStore.off('change', this.onChange, this);
  }

  onChange = () => {
    this.setState(this.getStoreState());
  };

  reloadAfterDatabaseDeleted() {
    let page = this.state.page;
    if (page > 1 && this.state.dbList.length === 1) {
      //show previous page if the db deleted was the only one in the page
      page = page - 1;
    }
    Actions.init({
      page,
      limit: this.state.limit
    });
  }

  state = this.getStoreState();

  render() {
    const { loading, dbList } = this.state;

    return (
      <DatabaseTable
        showDeleteDatabaseModal={this.state.showDeleteDatabaseModal}
        dbList={dbList}
        loading={loading}
        showPartitionedColumn={this.state.showPartitionedColumn}
        onDatabaseDeleted={this.reloadAfterDatabaseDeleted} />
    );
  }
}

class DatabaseTable extends React.Component {
  static propTypes = {
    dbList: PropTypes.array.isRequired,
    showDeleteDatabaseModal: PropTypes.object.isRequired,
    loading: PropTypes.bool.isRequired,
    showPartitionedColumn: PropTypes.bool.isRequired,
    onDatabaseDeleted: PropTypes.func
  };

  createRows = (dbList) => {
    return dbList.map((item, k) => {
      return (
        <DatabaseRow item={item} key={k} showPartitionedColumn={this.props.showPartitionedColumn} />
      );
    });
  };

  getExtensionColumns = () => {
    var cols = FauxtonAPI.getExtensions('DatabaseTable:head');
    return _.map(cols, (Item, index) => {
      return <Item key={index} />;
    });
  };

  showDeleteDatabaseModal = () => {
    ComponentsActions.showDeleteDatabaseModal({
      showDeleteModal: !this.props.showDeleteDatabaseModal.showDeleteModal
    });
  };

  render() {
    if (this.props.loading) {
      return (
        <div className="view">
          <Components.LoadLines />
        </div>
      );
    }

    const rows = this.createRows(this.props.dbList);
    return (
      <div className="view">
        <DeleteDatabaseModal
          showHide={this.showDeleteDatabaseModal}
          modalProps={this.props.showDeleteDatabaseModal}
          onSuccess={this.props.onDatabaseDeleted} />
        <table className="table table-striped fauxton-table-list databases">
          <thead>
            <tr>
              <th>Name</th>
              <th>Size</th>
              <th># of Docs</th>
              {this.props.showPartitionedColumn ? (<th>Partitioned</th>) : null}
              {this.getExtensionColumns()}
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {rows}
          </tbody>
        </table>
      </div>
    );
  }
}

class DatabaseRow extends React.Component {
  static propTypes = {
    item: PropTypes.shape({
      id: PropTypes.string.isRequired,
      encodedId: PropTypes.string.isRequired,
      url: PropTypes.string.isRequired,
      failed: PropTypes.bool.isRequired,
      dataSize: PropTypes.string,
      docCount: PropTypes.number,
      docDelCount: PropTypes.number,
      isPartitioned: PropTypes.bool,
      showTombstoneWarning: PropTypes.bool
    }).isRequired,
    showPartitionedColumn: PropTypes.bool.isRequired
  };

  getExtensionColumns = (row) => {
    var cols = FauxtonAPI.getExtensions('DatabaseTable:databaseRow');
    return _.map(cols, (Item, index) => {
      return <Item row={row} key={index} />;
    });
  };

  showDeleteDatabaseModal = (name) => {
    ComponentsActions.showDeleteDatabaseModal({ showDeleteModal: true, dbId: name });
  };

  render() {
    const {
      item
    } = this.props;

    const { encodedId, id, url, dataSize, docCount, docDelCount, showTombstoneWarning, failed, isPartitioned } = item;
    const tombStoneWarning = showTombstoneWarning ?
      (<GraveyardInfo docCount={docCount} docDelCount={docDelCount} />) : null;

    // if the row status failed to load, inform the user
    if (failed) {
      return (
        <tr>
          <td data-name="database-load-fail-name">{id}</td>
          <td colSpan="4" className="database-load-fail">This database failed to load.</td>
        </tr>
      );
    }
    const partitionedCol = this.props.showPartitionedColumn ?
      (<td>{isPartitioned ? 'Yes' : 'No'}</td>) :
      null;
    return (
      <tr>
        <td>
          <a href={url}>{id}</a>
        </td>
        <td>{dataSize}</td>
        <td>{docCount} {tombStoneWarning}</td>
        {partitionedCol}
        {this.getExtensionColumns(item)}

        <td className="database-actions">
          <ToolbarButton icon="fonticon-replicate" title={`Replicate ${id}`} aria-label={`Replicate ${id}`} onClick={() => FauxtonAPI.navigate("#/replication/_create/" + encodedId)} />
          <ToolbarButton icon="fonticon-lock" title={`Set permission for ${id}`} aria-label={`Set permission for ${id}`} onClick={() => FauxtonAPI.navigate("#/database/" + encodedId + "/permissions")} />
          <ToolbarButton icon="fonticon-trash" title={`Delete ${id}`} aria-label={`Delete ${id}`} onClick={this.showDeleteDatabaseModal.bind(this, id, encodedId)} />
        </td>
      </tr>
    );
  }
}

const GraveyardInfo = ({ docCount, docDelCount }) => {
  const graveyardTitle = `This database has just ${docCount} docs and ${docDelCount} deleted docs`;
  const tooltip = <Tooltip id="graveyard-tooltip">{graveyardTitle}</Tooltip>;

  return (
    <OverlayTrigger placement="top" overlay={tooltip}>
      <i className="js-db-graveyard fonticon fonticon-attention-circled" title={graveyardTitle}></i>
    </OverlayTrigger>
  );
};

class RightDatabasesHeader extends React.Component {

  constructor(props) {
    super(props);
    this.state = this.getStoreState();
  }

  getStoreState() {
    return {
      showPartitionedOption: databasesStore.isPartitionedDatabasesAvailable()
    };
  }

  componentDidMount() {
    databasesStore.on('change', this.onChange, this);
  }

  componentWillUnmount() {
    databasesStore.off('change', this.onChange, this);
  }

  onChange() {
    this.setState(this.getStoreState());
  }

  render() {
    return (
      <div className="header-right right-db-header flex-layout flex-row">
        <JumpToDatabaseWidget loadOptions={Actions.fetchAllDbsWithKey} />
        <AddDatabaseWidget
          showPartitionedOption={this.state.showPartitionedOption}
          partitionedDbHelpText={this.props.partitionedDbHelpText} />
      </div>
    );
  }
}

class AddDatabaseWidget extends React.Component {
  static defaultProps = {
    showPartitionedOption: false
  };

  static propTypes = {
    showPartitionedOption: PropTypes.bool.isRequired,
    partitionedDbHelpText: PropTypes.string
  };

  constructor(props) {
    super(props);
    this.state = {
      isPromptVisible: false,
      databaseName: '',
      partitionedSelected: false
    };

    this.onTrayToggle = this.onTrayToggle.bind(this);
    this.closeTray = this.closeTray.bind(this);
    this.focusInput = this.focusInput.bind(this);
    this.onKeyUpInInput = this.onKeyUpInInput.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onAddDatabase = this.onAddDatabase.bind(this);
    this.onTogglePartitioned = this.onTogglePartitioned.bind(this);
  }

  getI18nText(key) {
    if (this.props.i18n) {
      return this.props.i18n[key];
    }
    return '';
  }

  onTrayToggle() {
    this.setState({ isPromptVisible: !this.state.isPromptVisible });
  }

  closeTray() {
    this.setState({ isPromptVisible: false });
  }

  focusInput() {
    this.newDbName.focus();
  }

  onKeyUpInInput(e) {
    if (e.which === 13) {
      this.onAddDatabase();
    }
  }

  onChange(e) {
    this.setState({ databaseName: e.target.value });
  }

  onAddDatabase() {
    const partitioned = this.props.showPartitionedOption ?
      this.state.partitionedSelected :
      undefined;

    Actions.createNewDatabase(
      this.state.databaseName,
      partitioned
    );
  }

  onTogglePartitioned() {
    this.setState({ partitionedSelected: !this.state.partitionedSelected });
  }

  partitionedOption() {
    if (!this.props.showPartitionedOption) {
      return null;
    }
    const partitionedDbHelp = this.props.partitionedDbHelpText ? (
      <Accordion className='partitioned-db-help'>
        <AccordionItem title='Which should I choose?'>
          <p dangerouslySetInnerHTML={{__html: this.props.partitionedDbHelpText}} />
        </AccordionItem>
      </Accordion>
    ) : null;
    return (
      <Form.Group className="mb-3" controlId="formBasicCheckbox">
        <Form.Label>Partitioning</Form.Label>
        <div key="partition-radio" className="mb-3">
          <Form.Check
            label="Non-partitioned - recommended for most workloads"
            name="group1"
            type="radio"
            id="non-partitioned-option"
            checked={this.state.partitionedSelected === false}
            onChange={this.onTogglePartitioned}
          />
          <Form.Check
            label="Partitioned"
            name="group1"
            type="radio"
            id="partitioned-option"
            checked={this.state.partitionedSelected === true}
            onChange={this.onTogglePartitioned}
          />
        </div>
        {partitionedDbHelp}
      </Form.Group>
    );
  }

  render() {
    const classNames = classnames('new-database-tray', {
      'new-database-tray--expanded': this.props.showPartitionedOption
    });

    return (
      <div>
        <ToggleHeaderButton
          selected={this.state.isPromptVisible}
          toggleCallback={this.onTrayToggle}
          containerClasses='header-control-box add-new-database-btn'
          title="Create Database"
          fonticon="fonticon-new-database"
          text="Create Database" />

        <TrayContents
          className={classNames}
          contentVisible={this.state.isPromptVisible}
          closeTray={this.closeTray}
          onEnter={this.focusInput} >
          <div className='tray-contents'>
            <div className='tray-header'>
              <h3 >Create Database</h3>
            </div>
            <div className='tray-body'>
              <Form>
                <Form.Group className="mb-3">
                  <Form.Label htmlFor="js-new-database-name">Database name</Form.Label>
                  <Form.Control
                    id="js-new-database-name"
                    ref={node => this.newDbName = node}
                    type="text"
                    value={this.state.databaseName}
                    onChange={this.onChange}
                    onKeyUp={this.onKeyUpInInput}
                    placeholder="database-name"/>
                </Form.Group>
                {this.partitionedOption()}
              </Form>
            </div>
            <ButtonGroup bsPrefix="tray-footer" >
              <Button variant='cf-cancel' id="js-cancel-create-database" onClick={this.closeTray}>
               Cancel
              </Button>
              <Button variant='cf-primary' id="js-create-database" onClick={this.onAddDatabase}>
                Create
              </Button>
            </ButtonGroup>
          </div>
        </TrayContents>
      </div>
    );
  }
}


const JumpToDatabaseWidget = ({ loadOptions }) => {
  return (
    <div data-name="jump-to-db" className="faux-header__searchboxwrapper">
      <div className="faux-header__searchboxcontainer">

        <Components.ThrottledReactSelectAsync
          placeholder="Database name"
          loadOptions={loadOptions}
          clearable={false}
          onChange={({ value: databaseName }) => {
            Actions.jumpToDatabase(databaseName);
          }} />

      </div>
    </div>
  );
};
JumpToDatabaseWidget.propTypes = {
  loadOptions: PropTypes.func.isRequired
};

class DatabasePagination extends React.Component {
  static defaultProps = {
    linkPath: '_all_dbs',
    store: databasesStore
  };

  getStoreState = (props) => {
    const { store } = props;

    return {
      totalAmountOfDatabases: store.getTotalAmountOfDatabases(),
      page: store.getPage(),
      limit: store.getLimit()
    };
  };

  componentDidMount() {
    const { store } = this.props;

    store.on('change', this.onChange, this);
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    this.setState(this.getStoreState(nextProps));
    if (this.props.store) {
      this.props.store.off('change', this.onChange, this);
    }
    const { store } = nextProps;
    store.on('change', this.onChange, this);
  }

  componentWillUnmount() {
    const { store } = this.props;
    store.off('change', this.onChange, this);
  }

  onChange = () => {
    this.setState(this.getStoreState(this.props));
  };

  state = this.getStoreState(this.props);

  onPageLinkClick(pageNumber) {
    const url = `#/${this.props.linkPath}?page=${pageNumber}&limit=${this.state.limit}`;
    FauxtonAPI.navigate(url);
  }

  onPerPageSelected(perPage) {
    const url = `#/${this.props.linkPath}?page=${this.state.page}&limit=${perPage}`;
    FauxtonAPI.navigate(url);
  }

  render() {
    const { limit, page, totalAmountOfDatabases } = this.state;
    const start = 1 + (page - 1) * limit;
    const end = Math.min(totalAmountOfDatabases, page * limit);
    const { children } = this.props;

    return (
      <footer className="all-db-footer pagination-footer">
        <div id="database-pagination">
          <FauxtonComponentsReact.Pagination
            page={page}
            total={totalAmountOfDatabases}
            perPage={limit}
            onClick={this.onPageLinkClick.bind(this)}
          />
        </div>
        <PerPageSelector label="Databases per page" perPage={limit} perPageChange={this.onPerPageSelected.bind(this)} />
        <div className="current-databases">
          Showing <span className="all-db-footer__range">{start}&ndash;{end}</span>
          &nbsp;of&nbsp;<span className="all-db-footer__total-db-count">{totalAmountOfDatabases}</span>
          &nbsp;databases.
        </div>
        <div className="custom-db-footer-item">
          {children}
        </div>
      </footer>
    );
  }
}

export default {
  DatabasesController: DatabasesController,
  DatabaseTable: DatabaseTable,
  DatabaseRow: DatabaseRow,
  RightDatabasesHeader: RightDatabasesHeader,
  GraveyardInfo: GraveyardInfo,
  AddDatabaseWidget: AddDatabaseWidget,
  JumpToDatabaseWidget: JumpToDatabaseWidget,
  DatabasePagination: DatabasePagination
};

