import React from 'react';
import PropTypes from 'prop-types';
import { Container, Spinner, Navbar, Nav } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import camelCase from 'lodash/camelCase';
import { Helmet } from 'react-helmet';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';

import faviconBroken from '../img/push-health-broken.png';
import faviconOk from '../img/push-health-ok.png';
import ErrorMessages from '../shared/ErrorMessages';
import PushModel from '../models/push';
import RepositoryModel from '../models/repository';
import StatusProgress from '../shared/StatusProgress';
import { scrollToLine } from '../helpers/utils';
import { resultColorMap, getIcon } from '../helpers/display';
import {
  createQueryParams,
  parseQueryParams,
  updateQueryParams,
} from '../helpers/url';
import InputFilter from '../shared/InputFilter';

import TestMetric from './TestMetric';
import JobListMetric from './JobListMetric';
import CommitHistory from './CommitHistory';

export default class Health extends React.PureComponent {
  constructor(props) {
    super(props);

    const params = new URLSearchParams(props.location.search);

    this.state = {
      revision: params.get('revision'),
      repo: params.get('repo'),
      currentRepo: null,
      metrics: {},
      jobs: null,
      result: null,
      failureMessage: null,
      defaultTabIndex: 0,
      testGroup: params.get('testGroup') || '',
      selectedTest: params.get('selectedTest') || '',
      selectedTaskId: params.get('selectedTaskId') || '',
      selectedJobName: params.get('selectedJobName') || '',
      searchStr: params.get('searchStr') || '',
      regressionsOrderBy: params.get('regressionsOrderBy') || 'count',
      regressionsGroupBy: params.get('regressionsGroupBy') || 'path',
      knownIssuesOrderBy: params.get('knownIssuesOrderBy') || 'count',
      knownIssuesGroupBy: params.get('knownIssuesGroupBy') || 'path',
    };
  }

  async componentDidMount() {
    const { repo, testGroup } = this.state;
    const { location } = this.props;
    const {
      metrics: { linting, builds, tests },
    } = await this.updatePushHealth();

    const params = parseQueryParams(location.search);
    let defaultTabIndex;

    if (params.tab !== undefined) {
      defaultTabIndex = ['linting', 'builds', 'tests'].findIndex(
        (metric) => metric === params.tab,
      );
    } else if (testGroup) {
      defaultTabIndex = 2;
    } else {
      defaultTabIndex = [linting, builds, tests].findIndex(
        (metric) => metric.result === 'fail',
      );
    }

    const repos = await RepositoryModel.getList();
    const currentRepo = repos.find((repoObj) => repoObj.name === repo);

    this.setState({ defaultTabIndex, currentRepo });

    // Update the tests every two minutes.
    this.testTimerId = setInterval(() => this.updatePushHealth(), 120000);
    this.notificationsId = setInterval(() => {
      this.props.clearNotification();
    }, 4000);
  }

  componentWillUnmount() {
    clearInterval(this.testTimerId);
  }

  updateParamsAndState = (stateObj) => {
    const { location, history } = this.props;
    const newParams = {
      ...parseQueryParams(location.search),
      ...stateObj,
    };
    const queryString = createQueryParams(newParams);

    updateQueryParams(queryString, history, location);
    this.setState(stateObj);
  };

  updatePushHealth = async () => {
    const { repo, revision, status } = this.state;

    if (status) {
      const { running, pending, completed } = status;

      if (completed > 0 && pending === 0 && running === 0) {
        clearInterval(this.testTimerId);
        return;
      }
    }

    const { data, failureStatus } = await PushModel.getHealth(repo, revision);
    const newState = !failureStatus ? data : { failureMessage: data };

    this.setState(newState);
    return newState;
  };

  setExpanded = (metricName, expanded) => {
    const root = camelCase(metricName);
    const key = `${root}Expanded`;
    const { [key]: oldExpanded } = this.state;

    if (oldExpanded !== expanded) {
      this.setState({
        [key]: expanded,
      });
    } else if (expanded) {
      scrollToLine(`#${root}Metric`, 0, 0, {
        behavior: 'smooth',
        block: 'center',
      });
    }
  };

  filter = (searchStr) => {
    const { location, history } = this.props;
    const newParams = { ...parseQueryParams(location.search), searchStr };

    if (!searchStr.length) {
      delete newParams.searchStr;
    }

    const queryString = createQueryParams(newParams);

    updateQueryParams(queryString, history, location);

    this.setState({ searchStr });
  };

  render() {
    const {
      metrics,
      result,
      repo,
      revision,
      jobs,
      failureMessage,
      status,
      searchStr,
      currentRepo,
      testGroup,
      selectedTest,
      defaultTabIndex,
      selectedTaskId,
      selectedJobName,
      regressionsOrderBy,
      regressionsGroupBy,
      knownIssuesOrderBy,
      knownIssuesGroupBy,
    } = this.state;
    const { tests, commitHistory, linting, builds } = metrics;

    const { notify } = this.props;
    return (
      <React.Fragment>
        <Navbar color="light" light expand="sm" className="w-100">
          {!!tests && (
            <Nav className="mb-2 pt-2 pl-3 justify-content-between w-100">
              <span />
              <span className="mr-2 d-flex">
                <InputFilter
                  updateFilterText={this.filter}
                  placeholder="filter path or platform"
                />
              </span>
            </Nav>
          )}
        </Navbar>
        <Helmet>
          <link
            rel="shortcut icon"
            href={result === 'fail' ? faviconBroken : faviconOk}
          />
          <title>{`[${
            (status && status.testfailed) || 0
          } failures] Push Health`}</title>
        </Helmet>
        <Container fluid className="mt-2 mb-5 max-width-default">
          {!!tests && !!currentRepo && (
            <React.Fragment>
              <div className="d-flex my-5">
                <StatusProgress
                  counts={status}
                  customStyle="progress-relative"
                />
                <div className="mt-4 ml-2">
                  {commitHistory.details && (
                    <CommitHistory
                      history={commitHistory.details}
                      revision={revision}
                      currentRepo={currentRepo}
                      compareWithParent={this.compareWithParent}
                    />
                  )}
                </div>
              </div>
              <div className="mb-3" />
              <Tabs
                className="w-100 h-100 mr-5 mt-2"
                selectedTabClassName="selected-detail-tab"
                defaultIndex={defaultTabIndex}
              >
                <TabList className="font-weight-500 text-secondary d-flex justify-content-end border-bottom font-size-18">
                  {linting.result !== 'none' && (
                    <Tab className="pb-2 list-inline-item ml-4 pointable">
                      <span className="text-success">
                        <FontAwesomeIcon
                          icon={getIcon(linting.result)}
                          className={`mr-1 text-${
                            resultColorMap[linting.result]
                          }`}
                        />
                      </span>
                      Linting
                    </Tab>
                  )}
                  {builds.result !== 'none' && (
                    <Tab className="list-inline-item ml-4 pointable">
                      <FontAwesomeIcon
                        icon={getIcon(builds.result)}
                        className={`mr-1 text-${resultColorMap[builds.result]}`}
                      />
                      Builds
                    </Tab>
                  )}
                  {tests.result !== 'none' && (
                    <Tab className="list-inline-item ml-4 pointable">
                      <FontAwesomeIcon
                        fill={resultColorMap[tests.result]}
                        icon={getIcon(tests.result)}
                        className={`mr-1 text-${resultColorMap[tests.result]}`}
                      />
                      Tests
                    </Tab>
                  )}
                </TabList>
                <div>
                  <TabPanel>
                    <JobListMetric
                      data={linting}
                      currentRepo={currentRepo}
                      revision={revision}
                      setExpanded={this.setExpanded}
                      updateParamsAndState={this.updateParamsAndState}
                      notify={notify}
                      selectedTaskId={selectedTaskId}
                      selectedJobName={selectedJobName}
                    />
                  </TabPanel>
                  <TabPanel>
                    <JobListMetric
                      data={builds}
                      currentRepo={currentRepo}
                      revision={revision}
                      setExpanded={this.setExpanded}
                      updateParamsAndState={this.updateParamsAndState}
                      notify={notify}
                      selectedTaskId={selectedTaskId}
                      selectedJobName={selectedJobName}
                    />
                  </TabPanel>
                  <TabPanel>
                    <TestMetric
                      jobs={jobs}
                      data={tests}
                      repo={repo}
                      currentRepo={currentRepo}
                      revision={revision}
                      notify={notify}
                      setExpanded={this.setExpanded}
                      searchStr={searchStr}
                      testGroup={testGroup}
                      selectedTest={selectedTest}
                      regressionsOrderBy={regressionsOrderBy}
                      regressionsGroupBy={regressionsGroupBy}
                      knownIssuesOrderBy={knownIssuesOrderBy}
                      knownIssuesGroupBy={knownIssuesGroupBy}
                      selectedTaskId={selectedTaskId}
                      selectedJobName={selectedJobName}
                      updateParamsAndState={this.updateParamsAndState}
                      investigateTest={this.investigateTest}
                      unInvestigateTest={this.unInvestigateTest}
                      updatePushHealth={this.updatePushHealth}
                    />
                  </TabPanel>
                </div>
              </Tabs>
            </React.Fragment>
          )}
          {failureMessage && <ErrorMessages failureMessage={failureMessage} />}
          {!failureMessage && !tests && (
            <h4>
              <Spinner />
              <span className="ml-2 pb-1">Gathering health data...</span>
            </h4>
          )}
        </Container>
      </React.Fragment>
    );
  }
}

Health.propTypes = {
  location: PropTypes.shape({}).isRequired,
};
