import { LoadingSpinner } from '@sourcegraph/react-loading-spinner';
import { upperFirst } from 'lodash';
import AddIcon from 'mdi-react/AddIcon';
import CheckIcon from 'mdi-react/CheckIcon';
import CloseIcon from 'mdi-react/CloseIcon';
import CloudOutlineIcon from 'mdi-react/CloudOutlineIcon';
import SettingsIcon from 'mdi-react/SettingsIcon';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Subject, Subscription } from 'rxjs';
import { catchError, mergeMap, switchMap } from 'rxjs/operators';
import { FilteredConnection, } from '../components/FilteredConnection';
import { Form } from '../components/Form';
import { PageTitle } from '../components/PageTitle';
import { RepoLink } from '../repo/RepoLink';
import { refreshSiteFlags } from '../site/backend';
import { eventLogger } from '../tracking/eventLogger';
import { addRepository, fetchAllRepositoriesAndPollIfAnyCloning, setAllRepositoriesEnabled, setRepositoryEnabled, updateAllMirrorRepositories, updateMirrorRepository, } from './backend';
class RepositoryNode extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = {
            loading: false,
        };
        this.enableRepository = () => this.setRepositoryEnabled(true);
        this.disableRepository = () => this.setRepositoryEnabled(false);
    }
    render() {
        return (React.createElement("li", { className: "repository-node list-group-item py-2" },
            React.createElement("div", { className: "d-flex align-items-center justify-content-between" },
                React.createElement("div", null,
                    React.createElement(RepoLink, { repoPath: this.props.node.name, to: this.props.node.url }),
                    this.props.node.enabled ? (React.createElement("small", { "data-tooltip": "Access to this repository is enabled. All users can view and search it.", className: "ml-2 text-success" },
                        React.createElement(CheckIcon, { className: "icon-inline" }),
                        "Enabled")) : (React.createElement("small", { "data-tooltip": "Access to this repository is disabled. Enable access to it to allow users to view and search it.", className: "ml-2 text-danger" },
                        React.createElement(CloseIcon, { className: "icon-inline" }),
                        "Disabled")),
                    this.props.node.mirrorInfo.cloneInProgress && (React.createElement("small", { className: "ml-2 text-success" },
                        React.createElement(LoadingSpinner, { className: "icon-inline" }),
                        " Cloning")),
                    this.props.node.enabled &&
                        !this.props.node.mirrorInfo.cloneInProgress &&
                        !this.props.node.mirrorInfo.cloned && (React.createElement("small", { className: "ml-2 text-muted", "data-tooltip": "Visit the repository to clone it. See its mirroring settings for diagnostics." },
                        React.createElement(CloudOutlineIcon, { className: "icon-inline" }),
                        " Not yet cloned"))),
                React.createElement("div", { className: "repository-node__actions" },
                    React.createElement(Link, { className: "btn btn-secondary btn-sm", to: `/${this.props.node.name}/-/settings`, "data-tooltip": "Repository settings" },
                        React.createElement(SettingsIcon, { className: "icon-inline" }),
                        " Settings"),
                    ' ',
                    this.props.node.enabled ? (React.createElement("button", { className: "btn btn-secondary btn-sm", onClick: this.disableRepository, disabled: this.state.loading, "data-tooltip": "Disable access to the repository. Users will be unable to view and search it." }, "Disable")) : (React.createElement("button", { className: "btn btn-success btn-sm", onClick: this.enableRepository, disabled: this.state.loading, "data-tooltip": "Enable access to the repository. Users will be able to view and search it." }, "Enable")))),
            this.state.errorDescription && (React.createElement("div", { className: "alert alert-danger mt-2" }, this.state.errorDescription))));
    }
    setRepositoryEnabled(enabled) {
        this.setState({
            errorDescription: undefined,
            loading: true,
        });
        const promises = [setRepositoryEnabled(this.props.node.id, enabled).toPromise()];
        if (enabled) {
            promises.push(updateMirrorRepository({ repository: this.props.node.id }).toPromise());
        }
        Promise.all(promises).then(() => {
            if (this.props.onDidUpdate) {
                this.props.onDidUpdate();
            }
            this.setState({ loading: false });
        }, err => this.setState({ loading: false, errorDescription: err.message }));
    }
}
/**
 * A form for adding a public repository (for now just from https://github.com)
 */
class AddPublicRepositoryForm extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = {
            repoName: '',
            error: undefined,
            success: undefined,
        };
        this.inputElement = null;
        this.subscriptions = new Subscription();
        this.submits = new Subject();
        this.onPublicRepositoryNameFieldChange = (e) => {
            this.setState({ repoName: e.target.value });
        };
        this.onSubmit = (event) => {
            event.preventDefault();
            event.stopPropagation();
            this.submits.next({ repoName: this.state.repoName });
        };
    }
    componentDidMount() {
        this.subscriptions.add(this.submits
            .pipe(mergeMap(({ repoName }) => addRepository(`github.com/${repoName}`).pipe(switchMap(({ id }) => setRepositoryEnabled(id, true)), catchError(error => {
            console.error(error);
            eventLogger.log('PublicRepositoryAdditionFailed', {
                repositories: { code_host: 'github' },
            });
            this.setState({ success: undefined, error: error.message });
            return [];
        }))))
            .subscribe(() => {
            eventLogger.log('PublicRepositoryAdded', { repositories: { code_host: 'github' } });
            this.setState({ repoName: '', success: 'Repository added', error: undefined });
            setTimeout(() => this.setState({ success: undefined }), 1000);
            if (this.inputElement) {
                this.inputElement.focus();
            }
            this.props.onSuccess();
        }));
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    render() {
        return (React.createElement("div", { className: "add-public-repository-form" },
            React.createElement("h3", { className: "add-public-repository-form__title" }, "Add a public repository"),
            React.createElement(Form, { className: "add-public-repository-form__form", onSubmit: this.onSubmit },
                React.createElement("div", { className: "add-public-repository-form__input-scope" },
                    React.createElement("span", { className: "add-public-repository-form__input-scope-text" },
                        'https://github.com',
                        "/")),
                React.createElement("div", { className: "add-public-repository-form__input-container" },
                    React.createElement("input", { type: "input", className: "add-public-repository-form__input-container-input-text form-control", value: this.state.repoName, onChange: this.onPublicRepositoryNameFieldChange, placeholder: "organization/repository", autoFocus: true, ref: input => (this.inputElement = input) })),
                React.createElement("button", { className: "add-public-repository-form__add-button btn btn-primary", type: "submit", disabled: this.state.repoName === '' }, "Add")),
            this.state.error && (React.createElement("div", { className: "alert alert-danger add-public-repository-form__alert" }, upperFirst(this.state.error))),
            this.state.success && (React.createElement("div", { className: "alert alert-success add-public-repository-form__alert" }, this.state.success))));
    }
}
class FilteredRepositoryConnection extends FilteredConnection {
}
/**
 * A page displaying the repositories on this site.
 */
export class SiteAdminRepositoriesPage extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = {
            addPublicRepositoryFormVisible: false,
        };
        this.repositoryUpdates = new Subject();
        this.queryRepositories = (args) => fetchAllRepositoriesAndPollIfAnyCloning(Object.assign({}, args));
        this.onDidUpdateRepository = () => this.repositoryUpdates.next();
        this.toggleAddPublicRepositoryForm = () => {
            eventLogger.log('AddPublicRepositoryFormClicked');
            this.setState(state => ({ addPublicRepositoryFormVisible: !state.addPublicRepositoryFormVisible }));
        };
        this.enableAllRepostiories = () => this.setAllRepositoriesEnabled(true);
        this.disableAllRepostiories = () => this.setAllRepositoriesEnabled(false);
    }
    componentDidMount() {
        eventLogger.logViewEvent('SiteAdminRepos');
        // Refresh global alert about enabling repositories when the user visits here.
        refreshSiteFlags()
            .toPromise()
            .then(null, err => console.error(err));
    }
    componentWillUnmount() {
        // Remove global alert about enabling repositories when the user navigates away from here.
        refreshSiteFlags()
            .toPromise()
            .then(null, err => console.error(err));
    }
    render() {
        const nodeProps = {
            onDidUpdate: this.onDidUpdateRepository,
        };
        return (React.createElement("div", { className: "site-admin-repositories-page" },
            React.createElement(PageTitle, { title: "Repositories - Admin" }),
            React.createElement("h2", null, "Repositories"),
            React.createElement("div", null,
                React.createElement("button", { onClick: this.toggleAddPublicRepositoryForm, className: `btn ${this.state.addPublicRepositoryFormVisible ? 'btn-secondary' : 'btn-primary'}` }, this.state.addPublicRepositoryFormVisible ? (React.createElement(React.Fragment, null,
                    React.createElement(CloseIcon, { className: "icon-inline" }),
                    " Close")) : (React.createElement(React.Fragment, null,
                    React.createElement(AddIcon, { className: "icon-inline" }),
                    " Add public GitHub.com repository"))),
                ' ',
                React.createElement(Link, { to: "/site-admin/configuration", className: "btn btn-secondary" },
                    React.createElement(SettingsIcon, { className: "icon-inline" }),
                    " Configure repositories")),
            this.state.addPublicRepositoryFormVisible && (React.createElement(AddPublicRepositoryForm, { onSuccess: this.onDidUpdateRepository })),
            React.createElement(FilteredRepositoryConnection, { className: "list-group list-group-flush mt-3", noun: "repository", pluralNoun: "repositories", queryConnection: this.queryRepositories, nodeComponent: RepositoryNode, nodeComponentProps: nodeProps, updates: this.repositoryUpdates, filters: SiteAdminRepositoriesPage.FILTERS, history: this.props.history, location: this.props.location }),
            !window.context.sourcegraphDotComMode && (React.createElement("div", { className: "my-4" },
                React.createElement("button", { className: "btn btn-secondary", onClick: this.disableAllRepostiories }, "Disable all"),
                ' ',
                React.createElement("button", { className: "btn btn-secondary", onClick: this.enableAllRepostiories }, "Enable and clone all")))));
    }
    setAllRepositoriesEnabled(enabled) {
        if (enabled &&
            !confirm(`Enabling and cloning all repositories may take some time and use significant resources. This will enable and clone all accessible repositories, and is not limited to your current search filter. Enable and clone all repositories?`)) {
            return;
        }
        eventLogger.log(enabled ? 'EnableAllReposClicked' : 'DisableAllReposClicked');
        const promises = [setAllRepositoriesEnabled(enabled).toPromise()];
        if (enabled) {
            promises.push(updateAllMirrorRepositories().toPromise());
        }
        Promise.all(promises).then(this.onDidUpdateRepository, 
        // If one (or more) repositories fail, still update the UI before re-throwing
        err => {
            this.onDidUpdateRepository();
            throw err;
        });
    }
}
SiteAdminRepositoriesPage.FILTERS = [
    {
        label: 'All',
        id: 'all',
        tooltip: 'Show all repositories',
        args: { enabled: true, disabled: true },
    },
    {
        label: 'Enabled',
        id: 'enabled',
        tooltip: 'Show access-enabled repositories only',
        args: { enabled: true, disabled: false },
    },
    {
        label: 'Disabled',
        id: 'disabled',
        tooltip: 'Show access-disabled repositories only',
        args: { enabled: false, disabled: true },
    },
    {
        label: 'Cloned',
        id: 'cloned',
        tooltip: 'Show cloned repositories only',
        args: { disabled: true, cloned: true, cloneInProgress: false, notCloned: false },
    },
    {
        label: 'Cloning',
        id: 'cloning',
        tooltip: 'Show only repositories that are currently being cloned',
        args: { disabled: true, cloned: false, cloneInProgress: true, notCloned: false },
    },
    {
        label: 'Not cloned',
        id: 'not-cloned',
        tooltip: 'Show only enabled repositories that have not been cloned yet',
        args: { cloned: false, cloneInProgress: false, notCloned: true },
    },
    {
        label: 'Needs index',
        id: 'needs-index',
        tooltip: 'Show only repositories that need to be indexed',
        args: { indexed: false },
    },
];
//# sourceMappingURL=SiteAdminRepositoriesPage.js.map