import _ from 'lodash';
import ApiHelpers from './util/ApiHelpers.jsx';
import {friendlyTitle} from './util/Utils.js';
import {Link} from 'react-router-dom';
import PropTypes from 'prop-types';
import React from 'react';
import ReactRouterPropTypes from 'react-router-prop-types';
import SocialLinks from './SocialLinks.jsx';
import Version from './Version.jsx';
import {withContext} from './util/AppContext.jsx';
import {Badge, Form, Icon, Layout, Menu, Select} from 'antd';
import {
  excludeResourcesFromRollup,
  getSuccessRateClassification,
  processMultiResourceRollup,
  processSingleResourceRollup
} from './util/MetricUtils.js';
import {linkerdLogoOnly, linkerdWordLogo} from './util/SvgWrappers.jsx';
import './../../css/sidebar.css';

const classificationLabels = {
  good: "success",
  neutral: "warning",
  bad: "error"
};

class Sidebar extends React.Component {
  static defaultProps = {
    productName: 'controller'
  }

  static propTypes = {
    api: PropTypes.shape({
      PrefixedLink: PropTypes.func.isRequired,
    }).isRequired,
    location: ReactRouterPropTypes.location.isRequired,
    pathPrefix: PropTypes.string.isRequired,
    productName: PropTypes.string,
    releaseVersion: PropTypes.string.isRequired,
    uuid: PropTypes.string.isRequired,
  }

  constructor(props) {
    super(props);
    this.api = this.props.api;
    this.toggleCollapse = this.toggleCollapse.bind(this);
    this.loadFromServer = this.loadFromServer.bind(this);
    this.handleApiError = this.handleApiError.bind(this);
    this.handleNamespaceSelector = this.handleNamespaceSelector.bind(this);

    this.state = this.getInitialState();
  }

  getInitialState() {
    return {
      pollingInterval: 12000,
      initialCollapse: false,
      collapsed: true,
      error: null,
      latestVersion: '',
      isLatest: true,
      pendingRequests: false,
      namespaceFilter: "all"
    };
  }

  componentDidMount() {
    this.startServerPolling();
  }

  componentWillUnmount() {
    // the Sidebar never unmounts, but if something ever does, we should take care of it
    this.stopServerPolling();
  }

  startServerPolling() {
    this.loadFromServer();
    this.timerId = window.setInterval(this.loadFromServer, this.state.pollingInterval);
  }

  stopServerPolling() {
    window.clearInterval(this.timerId);
    this.api.cancelCurrentRequests();
  }


  loadFromServer() {
    if (this.state.pendingRequests) {
      return; // don't make more requests if the ones we sent haven't completed
    }
    this.setState({ pendingRequests: true });

    let versionUrl = `https://versioncheck.linkerd.io/version.json?version=${this.props.releaseVersion}?uuid=${this.props.uuid}`;
    this.api.setCurrentRequests([
      ApiHelpers("").fetch(versionUrl),
      this.api.fetchMetrics(this.api.urlsForResource("all")),
      this.api.fetchMetrics(this.api.urlsForResource("namespace"))
    ]);

    // expose serverPromise for testing
    this.serverPromise = Promise.all(this.api.getCurrentPromises())
      .then(([versionRsp, allRsp, nsRsp]) => {
        let allResourceGroups = processMultiResourceRollup(allRsp);
        let finalResourceGroups = excludeResourcesFromRollup(allResourceGroups, ["authority", "service"]);
        let nsStats = processSingleResourceRollup(nsRsp);
        let namespaces = _(nsStats).map('name').sortBy().value();

        this.setState({
          latestVersion: versionRsp.version,
          isLatest: versionRsp.version === this.props.releaseVersion,
          finalResourceGroups,
          namespaces,
          pendingRequests: false,
        });
      }).catch(this.handleApiError);
  }

  handleApiError(e) {
    this.setState({
      pendingRequests: false,
      error: e
    });
  }

  toggleCollapse() {
    if (this.state.initialCollapse) {
      // fix weird situation where toggleCollapsed is called on pageload,
      // causing the toggle states to be inconsistent. Don't toggle on the
      // very first call to toggleCollapse()
      this.setState({ initialCollapse: false});
    } else {
      this.setState({ collapsed: !this.state.collapsed });
    }
  }

  handleNamespaceSelector(value) {
    this.setState({namespaceFilter: value});
  }

  filterResourcesByNamespace(resources, namespace) {
    let resourceFilter = namespace === "all" ? r => r.added :
      r => r.namespace === namespace && r.added;

    return _.mapValues(resources, o => _.filter(o, resourceFilter));
  }



  render() {
    let normalizedPath = this.props.location.pathname.replace(this.props.pathPrefix, "");
    let PrefixedLink = this.api.PrefixedLink;
    let namespaces = [
      {value: "all", name: "All Namespaces"}
    ].concat(_.map(this.state.namespaces, ns => {
      return {value: ns, name: ns};
    }));
    let sidebarComponents = this.filterResourcesByNamespace(this.state.finalResourceGroups, this.state.namespaceFilter);
    return (
      <Layout.Sider
        width="260px"
        breakpoint="lg"
        collapsed={this.state.collapsed}
        collapsible={true}
        onCollapse={this.toggleCollapse}>
        <div className="sidebar">

          <div className={`sidebar-menu-header ${this.state.collapsed ? "collapsed" : ""}`}>
            <PrefixedLink to="/servicemesh">
              {this.state.collapsed ? linkerdLogoOnly : linkerdWordLogo}
            </PrefixedLink>
          </div>

          <Menu
            className="sidebar-menu"
            theme="dark"
            selectedKeys={[normalizedPath]}>
            <Menu.Item className="sidebar-menu-item" key="/servicemesh">
              <PrefixedLink to="/servicemesh">
                <Icon type="home" />
                <span>Service mesh</span>
              </PrefixedLink>
            </Menu.Item>

            <Menu.Item className="sidebar-menu-item" key="/namespaces">
              <PrefixedLink to="/namespaces">
                <Icon type="dashboard" />
                <span>Namespaces</span>
              </PrefixedLink>
            </Menu.Item>

            <Menu.Item className="sidebar-menu-item" key="/tap">
              <PrefixedLink to="/tap">
                <Icon type="filter" />
                <span>Tap</span>
              </PrefixedLink>
            </Menu.Item>

            <Menu.Item className="sidebar-menu-item" key="/top">
              <PrefixedLink to="/top">
                <Icon type="caret-up" />
                <span>Top</span>
              </PrefixedLink>
            </Menu.Item>

            <Menu.SubMenu
              className="sidebar-menu-item"
              key="byresource"
              title={<span className="sidebar-title"><Icon type="bars" />{this.state.collapsed ? "" : "Resources"}</span>}>
              <Menu.Item><PrefixedLink to="/authorities">Authorities</PrefixedLink></Menu.Item>
              <Menu.Item><PrefixedLink to="/deployments">Deployments</PrefixedLink></Menu.Item>
              <Menu.Item><PrefixedLink to="/pods">Pods</PrefixedLink></Menu.Item>
              <Menu.Item><PrefixedLink to="/replicationcontrollers">Replication Controllers</PrefixedLink></Menu.Item>
            </Menu.SubMenu>
            <Menu.Divider />
          </Menu>


          <Menu
            className="sidebar-menu"
            theme="dark"
            mode="inline"
            selectedKeys={[normalizedPath]}>
            { this.state.collapsed ? null : (
              <Menu.Item className="sidebar-menu-item" key="/namespace-selector">
                <Form layout="inline">
                  <Form.Item>
                    <Select
                      defaultValue="All Namespaces"
                      dropdownMatchSelectWidth={true}
                      onChange={this.handleNamespaceSelector}>
                      {
                      _.map(namespaces, label => {
                        return (
                          <Select.Option key={label.value} value={label.value}>{label.name}</Select.Option>
                        );
                      })
                    }
                    </Select>
                  </Form.Item>
                </Form>
              </Menu.Item>
            )}

            {this.state.collapsed ? null :
              _.map(_.keys(sidebarComponents).sort(), resourceName => {
                return (
                  <Menu.SubMenu
                    className="sidebar-menu-item"
                    key={resourceName}
                    disabled={_.isEmpty(sidebarComponents[resourceName])}
                    title={<span>{friendlyTitle(resourceName).plural}</span>}>
                    {
                      _.map(_.sortBy(sidebarComponents[resourceName], r => `${r.namespace}/${r.name}`), r => {
                        // only display resources that have been meshed
                          return (
                            <Menu.Item
                              className="sidebar-submenu-item"
                              title={`${r.namespace}/${r.name}`}
                              key={this.api.generateResourceURL(r)}>
                              <div>
                                <PrefixedLink
                                  to={this.api.generateResourceURL(r)}>
                                  {`${r.namespace}/${r.name}`}
                                </PrefixedLink>
                                <Badge status={getSuccessRateClassification(r.successRate, classificationLabels)} />
                              </div>
                            </Menu.Item>
                          );
                      })
                    }
                  </Menu.SubMenu>
                );
              })
            }
            <Menu.Item className="sidebar-menu-item" key="/docs">
              <Link to="https://linkerd.io/2/overview/" target="_blank">
                <Icon type="file-text" />
                <span>Documentation</span>
              </Link>
            </Menu.Item>

            { this.state.isLatest ? null : (
              <Menu.Item className="sidebar-menu-item" key="/update">
                <Link to="https://versioncheck.linkerd.io/update" target="_blank">
                  <Icon type="exclamation-circle-o" className="update" />
                  <span>Update {this.props.productName}</span>
                </Link>
              </Menu.Item>
            )}
          </Menu>

          { this.state.collapsed ? null : (
            <div className="sidebar-menu-footer">
              <SocialLinks />
              <Version
                isLatest={this.state.isLatest}
                latestVersion={this.state.latestVersion}
                releaseVersion={this.props.releaseVersion}
                error={this.state.error}
                uuid={this.props.uuid} />
            </div>
          )}
        </div>
      </Layout.Sider>
    );
  }
}

export default withContext(Sidebar);
