import { escapeRegExp, isEqual } from 'lodash';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Subject } from 'rxjs';
import { map } from 'rxjs/operators';
import { gql, queryGraphQL } from '../../backend/graphql';
import { FilteredConnection } from '../../components/FilteredConnection';
import { Form } from '../../components/Form';
import { PageTitle } from '../../components/PageTitle';
import { Timestamp } from '../../components/time/Timestamp';
import { buildSearchURLQuery, quoteIfNeeded, searchQueryForRepoRev } from '../../search';
import { eventLogger } from '../../tracking/eventLogger';
import { PersonLink } from '../../user/PersonLink';
import { UserAvatar } from '../../user/UserAvatar';
import { createAggregateError } from '../../util/errors';
import { memoizeObservable } from '../../util/memoize';
import { numberWithCommas, pluralize } from '../../util/strings';
const RepositoryContributorNode = ({ node, repoPath, revisionRange, after, path, }) => {
    const commit = node.commits.nodes[0];
    const query = [
        searchQueryForRepoRev(repoPath),
        'type:diff',
        `author:${quoteIfNeeded(node.person.email)}`,
        after ? `after:${quoteIfNeeded(after)}` : '',
        path ? `file:${quoteIfNeeded(escapeRegExp(path))}` : '',
    ]
        .join(' ')
        .replace(/\s+/, ' ');
    return (React.createElement("div", { className: "repository-contributor-node list-group-item py-2" },
        React.createElement("div", { className: "repository-contributor-node__person" },
            React.createElement(UserAvatar, { className: "icon-inline mr-2", user: node.person }),
            React.createElement(PersonLink, Object.assign({ userClassName: "font-weight-bold" }, node.person))),
        React.createElement("div", { className: "repository-contributor-node__commits" },
            React.createElement("div", { className: "repository-contributor-node__commit" }, commit && (React.createElement(React.Fragment, null,
                React.createElement(Timestamp, { date: commit.author.date }),
                ":",
                ' ',
                React.createElement(Link, { to: commit.url, className: "repository-contributor-node__commit-subject", "data-tooltip": "Most recent commit by contributor" }, commit.subject)))),
            React.createElement("div", { className: "repository-contributor-node__count" },
                React.createElement(Link, { to: `/search?${buildSearchURLQuery({
                        query,
                    })}`, className: "font-weight-bold", "data-tooltip": revisionRange &&
                        revisionRange.includes('..') &&
                        'All commits will be shown (revision end ranges are not yet supported)' },
                    numberWithCommas(node.count),
                    " ",
                    pluralize('commit', node.count))))));
};
const queryRepositoryContributors = memoizeObservable((args) => queryGraphQL(gql `
                query RepositoryContributors(
                    $repo: ID!
                    $first: Int
                    $revisionRange: String
                    $after: String
                    $path: String
                ) {
                    node(id: $repo) {
                        ... on Repository {
                            contributors(first: $first, revisionRange: $revisionRange, after: $after, path: $path) {
                                nodes {
                                    person {
                                        name
                                        displayName
                                        email
                                        avatarURL
                                        user {
                                            username
                                            url
                                        }
                                    }
                                    count
                                    commits(first: 1) {
                                        nodes {
                                            oid
                                            abbreviatedOID
                                            url
                                            subject
                                            author {
                                                date
                                            }
                                        }
                                    }
                                }
                                totalCount
                                pageInfo {
                                    hasNextPage
                                }
                            }
                        }
                    }
                }
            `, args).pipe(map(({ data, errors }) => {
    if (!data || !data.node || !data.node.contributors || errors) {
        throw createAggregateError(errors);
    }
    return data.node.contributors;
})), args => `${args.repo}:${args.first}:${args.revisionRange}:${args.after}:${args.path}`);
class FilteredContributorsConnection extends FilteredConnection {
}
/** A page that shows a repository's contributors. */
export class RepositoryStatsContributorsPage extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = this.getDerivedProps();
        this.specChanges = new Subject();
        this.onChange = e => {
            switch (e.currentTarget.id) {
                case RepositoryStatsContributorsPage.REVISION_RANGE_INPUT_ID:
                    this.setState({ revisionRange: e.currentTarget.value });
                    return;
                case RepositoryStatsContributorsPage.AFTER_INPUT_ID:
                    this.setState({ after: e.currentTarget.value });
                    return;
                case RepositoryStatsContributorsPage.PATH_INPUT_ID:
                    this.setState({ path: e.currentTarget.value });
                    return;
            }
        };
        this.onSubmit = e => {
            e.preventDefault();
            this.props.history.push({ search: this.urlQuery(this.state) });
        };
        this.onCancel = e => {
            e.preventDefault();
            this.setState(this.getDerivedProps());
        };
        this.queryRepositoryContributors = (args) => {
            const { revisionRange, after, path } = this.getDerivedProps();
            return queryRepositoryContributors(Object.assign({}, args, { repo: this.props.repo.id, revisionRange: revisionRange || undefined, after: after || undefined, path: path || undefined }));
        };
    }
    componentDidMount() {
        eventLogger.logViewEvent('RepositoryStatsContributors');
    }
    componentDidUpdate(prevProps) {
        const spec = this.getDerivedProps(this.props.location);
        const prevSpec = this.getDerivedProps(prevProps.location);
        if (!isEqual(spec, prevSpec)) {
            eventLogger.log('RepositoryStatsContributorsPropsUpdated');
            this.setState(spec);
            this.specChanges.next();
        }
    }
    getDerivedProps(location = this.props.location) {
        const query = new URLSearchParams(location.search);
        const revisionRange = query.get('revision-range');
        const after = query.get('after');
        const path = query.get('path');
        return { revisionRange, after, path };
    }
    render() {
        const { revisionRange, after, path } = this.getDerivedProps();
        // Whether the user has entered new option values that differ from what's in the URL query and has not yet
        // submitted the form.
        const equalOrEmpty = (a, b) => a === b || (!a && !b);
        const stateDiffers = !equalOrEmpty(this.state.revisionRange, revisionRange) ||
            !equalOrEmpty(this.state.after, after) ||
            !equalOrEmpty(this.state.path, path);
        return (React.createElement("div", { className: "repository-stats-page" },
            React.createElement(PageTitle, { title: "Contributors" }),
            React.createElement("div", { className: "card repository-stats-page__card repository-stats-page__card--form" },
                React.createElement("div", { className: "card-header" }, "Contributions filter"),
                React.createElement("div", { className: "card-body" },
                    React.createElement(Form, { onSubmit: this.onSubmit },
                        React.createElement("div", { className: "repository-stats-page__row form-inline" },
                            React.createElement("div", { className: "input-group mb-2 mr-sm-2" },
                                React.createElement("div", { className: "input-group-prepend" },
                                    React.createElement("label", { htmlFor: RepositoryStatsContributorsPage.AFTER_INPUT_ID, className: "input-group-text" }, "Time period")),
                                React.createElement("input", { type: "text", className: "form-control", name: "after", size: 12, id: RepositoryStatsContributorsPage.AFTER_INPUT_ID, value: this.state.after || '', placeholder: "All time", onChange: this.onChange }),
                                React.createElement("div", { className: "input-group-append" },
                                    React.createElement("div", { className: "btn-group" },
                                        React.createElement("button", { type: "button", className: `btn btn-secondary ${this.state.after === '7 days ago' ? 'active' : ''} repository-stats-page__btn-no-left-rounded-corners`, 
                                            // tslint:disable-next-line:jsx-no-lambda
                                            onClick: () => this.setStateAfterAndSubmit('7 days ago') }, "Last 7 days"),
                                        React.createElement("button", { type: "button", className: `btn btn-secondary ${this.state.after === '30 days ago' ? 'active' : ''}`, 
                                            // tslint:disable-next-line:jsx-no-lambda
                                            onClick: () => this.setStateAfterAndSubmit('30 days ago') }, "Last 30 days"),
                                        React.createElement("button", { type: "button", className: `btn btn-secondary ${this.state.after === '1 year ago' ? 'active' : ''}`, 
                                            // tslint:disable-next-line:jsx-no-lambda
                                            onClick: () => this.setStateAfterAndSubmit('1 year ago') }, "Last year"),
                                        React.createElement("button", { type: "button", className: `btn btn-secondary ${!this.state.after ? 'active' : ''}`, 
                                            // tslint:disable-next-line:jsx-no-lambda
                                            onClick: () => this.setStateAfterAndSubmit(null) }, "All time"))))),
                        React.createElement("div", { className: "repository-stats-page__row form-inline" },
                            React.createElement("div", { className: "input-group mt-2 mr-sm-2" },
                                React.createElement("div", { className: "input-group-prepend" },
                                    React.createElement("label", { htmlFor: RepositoryStatsContributorsPage.REVISION_RANGE_INPUT_ID, className: "input-group-text" }, "Revision range")),
                                React.createElement("input", { type: "text", className: "form-control", name: "revision-range", size: 18, id: RepositoryStatsContributorsPage.REVISION_RANGE_INPUT_ID, value: this.state.revisionRange || '', placeholder: "Default branch", onChange: this.onChange, autoCapitalize: "off", autoCorrect: "off", autoComplete: "off", spellCheck: false })),
                            React.createElement("div", { className: "input-group mt-2 mr-sm-2" },
                                React.createElement("div", { className: "input-group-prepend" },
                                    React.createElement("label", { htmlFor: RepositoryStatsContributorsPage.PATH_INPUT_ID, className: "input-group-text" }, "Path")),
                                React.createElement("input", { type: "text", className: "form-control", name: "path", size: 18, id: RepositoryStatsContributorsPage.PATH_INPUT_ID, value: this.state.path || '', placeholder: "All files", onChange: this.onChange, autoCapitalize: "off", autoCorrect: "off", autoComplete: "off", spellCheck: false })),
                            stateDiffers && (React.createElement("div", { className: "form-group mb-0" },
                                React.createElement("button", { type: "submit", className: "btn btn-primary mr-2 mt-2" }, "Update"),
                                React.createElement("button", { type: "reset", className: "btn btn-secondary mt-2", onClick: this.onCancel }, "Cancel"))))))),
            React.createElement(FilteredContributorsConnection, { listClassName: "list-group list-group-flush", noun: "contributor", pluralNoun: "contributors", queryConnection: this.queryRepositoryContributors, nodeComponent: RepositoryContributorNode, nodeComponentProps: {
                    repoPath: this.props.repo.name,
                    revisionRange,
                    after,
                    path,
                }, defaultFirst: 20, hideSearch: true, shouldUpdateURLQuery: false, updates: this.specChanges, history: this.props.history, location: this.props.location })));
    }
    urlQuery(spec) {
        const query = new URLSearchParams();
        if (spec.revisionRange) {
            query.set('revision-range', spec.revisionRange);
        }
        if (spec.after) {
            query.set('after', spec.after);
        }
        if (spec.path) {
            query.set('path', spec.path);
        }
        return query.toString();
    }
    setStateAfterAndSubmit(after) {
        this.setState({ after }, () => this.props.history.push({ search: this.urlQuery(this.state) }));
    }
}
RepositoryStatsContributorsPage.REVISION_RANGE_INPUT_ID = 'repository-stats-contributors-page__revision-range';
RepositoryStatsContributorsPage.AFTER_INPUT_ID = 'repository-stats-contributors-page__after';
RepositoryStatsContributorsPage.PATH_INPUT_ID = 'repository-stats-contributors-page__path';
//# sourceMappingURL=RepositoryStatsContributorsPage.js.map