import AlertCircleIcon from 'mdi-react/AlertCircleIcon';
import MapSearchIcon from 'mdi-react/MapSearchIcon';
import * as React from 'react';
import { Route, Switch } from 'react-router';
import { merge, Subject, Subscription } from 'rxjs';
import { catchError, distinctUntilChanged, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { parseBrowserRepoURL } from '.';
import { parseRepoRev, redirectToExternalHost } from '.';
import { HeroPage } from '../components/HeroPage';
import { searchQueryForRepoRev } from '../search';
import { queryUpdates } from '../search/input/QueryInput';
import { refreshConfiguration } from '../user/settings/backend';
import { isErrorLike } from '../util/errors';
import { GoToCodeHostAction } from './actions/GoToCodeHostAction';
import { EREPONOTFOUND, EREPOSEEOTHER, fetchRepository } from './backend';
import { RepositoryBranchesArea } from './branches/RepositoryBranchesArea';
import { RepositoryCommitPage } from './commit/RepositoryCommitPage';
import { RepositoryCompareArea } from './compare/RepositoryCompareArea';
import { RepositoryReleasesArea } from './releases/RepositoryReleasesArea';
import { RepoHeader } from './RepoHeader';
import { RepoHeaderContributionPortal } from './RepoHeaderContributionPortal';
import { RepoRevContainer } from './RepoRevContainer';
import { RepositoryErrorPage } from './RepositoryErrorPage';
import { RepositoryGitDataContainer } from './RepositoryGitDataContainer';
import { RepoSettingsArea } from './settings/RepoSettingsArea';
import { RepositoryStatsArea } from './stats/RepositoryStatsArea';
const RepoPageNotFound = () => (React.createElement(HeroPage, { icon: MapSearchIcon, title: "404: Not Found", subtitle: "The repository page was not found." }));
/**
 * Renders a horizontal bar and content for a repository page.
 */
export class RepoContainer extends React.Component {
    constructor(props) {
        super(props);
        this.routeMatchChanges = new Subject();
        this.repositoryUpdates = new Subject();
        this.repositoryAdds = new Subject();
        this.subscriptions = new Subscription();
        this.onDidUpdateRepository = (update) => this.repositoryUpdates.next(update);
        this.onDidAddRepository = () => this.repositoryAdds.next();
        this.onDidUpdateExternalLinks = (externalLinks) => this.setState({ externalLinks });
        this.onResolvedRevOrError = (v) => this.setState({ resolvedRevOrError: v });
        this.onRepoHeaderContributionsLifecyclePropsChange = (lifecycleProps) => this.setState({ repoHeaderContributionsLifecycleProps: lifecycleProps });
        this.state = Object.assign({}, parseURLPath(props.match.params.repoRevAndRest));
    }
    componentDidMount() {
        // Refresh the list of Sourcegraph extensions to use. This helps when a
        // language server gets automatically enabled after the first repository
        // in that language is added.
        refreshConfiguration()
            .toPromise()
            .catch(err => console.error(err));
        const parsedRouteChanges = this.routeMatchChanges.pipe(map(({ repoRevAndRest }) => parseURLPath(repoRevAndRest)));
        // Fetch repository.
        const repositoryChanges = parsedRouteChanges.pipe(map(({ repoPath }) => repoPath), distinctUntilChanged());
        this.subscriptions.add(merge(repositoryChanges, this.repositoryAdds.pipe(withLatestFrom(repositoryChanges), map(([, repoPath]) => repoPath)))
            .pipe(tap(() => this.setState({ repoOrError: undefined })), switchMap(repoPath => fetchRepository({ repoPath }).pipe(catchError(error => {
            switch (error.code) {
                case EREPOSEEOTHER:
                    redirectToExternalHost(error.redirectURL);
                    return [];
            }
            this.setState({ repoOrError: error });
            return [];
        }))))
            .subscribe(repo => {
            this.setState({ repoOrError: repo });
        }, err => {
            console.error(err);
        }));
        // Update header and other global state.
        this.subscriptions.add(parsedRouteChanges.subscribe(({ repoPath, rev, rawRev, rest }) => {
            this.setState({ repoPath, rev, rawRev, rest });
            queryUpdates.next(searchQueryForRepoRev(repoPath, rev));
        }));
        this.routeMatchChanges.next(this.props.match.params);
        // Merge in repository updates.
        this.subscriptions.add(this.repositoryUpdates.subscribe(update => this.setState(({ repoOrError }) => ({ repoOrError: Object.assign({}, repoOrError, update) }))));
    }
    componentWillReceiveProps(props) {
        if (props.match.params !== this.props.match.params) {
            this.routeMatchChanges.next(props.match.params);
        }
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    render() {
        if (!this.state.repoOrError) {
            // Render nothing while loading
            return null;
        }
        const { repoPath, filePath, position, range } = parseBrowserRepoURL(location.pathname + location.search + location.hash);
        const viewerCanAdminister = !!this.props.user && this.props.user.siteAdmin;
        if (isErrorLike(this.state.repoOrError)) {
            // Display error page
            switch (this.state.repoOrError.code) {
                case EREPONOTFOUND:
                    return (React.createElement(RepositoryErrorPage, { repo: repoPath, repoID: null, error: this.state.repoOrError, viewerCanAdminister: viewerCanAdminister, onDidAddRepository: this.onDidAddRepository }));
                default:
                    return React.createElement(HeroPage, { icon: AlertCircleIcon, title: "Error", subtitle: this.state.repoOrError.message });
            }
        }
        const repoMatchURL = `/${this.state.repoOrError.name}`;
        const transferProps = Object.assign({ repo: this.state.repoOrError, user: this.props.user, isLightTheme: this.props.isLightTheme, repoMatchURL, onHelpPopoverToggle: this.props.onHelpPopoverToggle, configurationCascade: this.props.configurationCascade, extensions: this.props.extensions, extensionsOnVisibleTextDocumentsChange: this.props.extensionsOnVisibleTextDocumentsChange, extensionsController: this.props.extensionsController }, this.state.repoHeaderContributionsLifecycleProps);
        const isSettingsPage = location.pathname === `${repoMatchURL}/-/settings` ||
            location.pathname.startsWith(`${repoMatchURL}/-/settings/`);
        return (React.createElement("div", { className: "repo-container w-100 d-flex flex-column" },
            React.createElement(RepoHeader, { actionButtons: this.props.repoHeaderActionButtons, rev: this.state.rev, repo: this.state.repoOrError, resolvedRev: this.state.resolvedRevOrError, extensions: this.props.extensions, extensionsController: this.props.extensionsController, onLifecyclePropsChange: this.onRepoHeaderContributionsLifecyclePropsChange, location: this.props.location, history: this.props.history }),
            React.createElement(RepoHeaderContributionPortal, Object.assign({ position: "right", key: "go-to-code-host", priority: 2, element: React.createElement(GoToCodeHostAction, { key: "go-to-code-host", repo: this.state.repoOrError, 
                    // We need a rev to generate code host URLs, since we don't have a default use HEAD.
                    rev: this.state.rev || 'HEAD', filePath: filePath, position: position, range: range, externalLinks: this.state.externalLinks }) }, this.state.repoHeaderContributionsLifecycleProps)),
            this.state.repoOrError.enabled || isSettingsPage ? (React.createElement(Switch, null,
                [
                    '',
                    `@${this.state.rawRev}`,
                    '/-/blob',
                    '/-/tree',
                    '/-/graph',
                    '/-/commits',
                ].map(routePath => (React.createElement(Route, { path: `${repoMatchURL}${routePath}`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                    , exact: routePath === '', 
                    // tslint:disable-next-line:jsx-no-lambda
                    render: routeComponentProps => (React.createElement(RepoRevContainer, Object.assign({}, routeComponentProps, transferProps, { routes: this.props.repoRevContainerRoutes, rev: this.state.rev || '', resolvedRevOrError: this.state.resolvedRevOrError, onResolvedRevOrError: this.onResolvedRevOrError, 
                        // must exactly match how the rev was encoded in the URL
                        routePrefix: `${repoMatchURL}${this.state.rawRev ? `@${this.state.rawRev}` : ''}` }))) }))),
                React.createElement(Route, { path: `${repoMatchURL}/-/commit/:revspec+`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                    , 
                    // tslint:disable-next-line:jsx-no-lambda
                    render: routeComponentProps => (React.createElement(RepositoryGitDataContainer, { repoPath: this.state.repoPath },
                        React.createElement(RepositoryCommitPage, Object.assign({}, routeComponentProps, transferProps, { onDidUpdateExternalLinks: this.onDidUpdateExternalLinks })))) }),
                React.createElement(Route, { path: `${repoMatchURL}/-/branches`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                    , 
                    // tslint:disable-next-line:jsx-no-lambda
                    render: routeComponentProps => (React.createElement(RepositoryGitDataContainer, { repoPath: this.state.repoPath },
                        React.createElement(RepositoryBranchesArea, Object.assign({}, routeComponentProps, transferProps)))) }),
                React.createElement(Route, { path: `${repoMatchURL}/-/tags`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                    , 
                    // tslint:disable-next-line:jsx-no-lambda
                    render: routeComponentProps => (React.createElement(RepositoryGitDataContainer, { repoPath: this.state.repoPath },
                        React.createElement(RepositoryReleasesArea, Object.assign({}, routeComponentProps, transferProps)))) }),
                React.createElement(Route, { path: `${repoMatchURL}/-/compare/:spec*`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                    , 
                    // tslint:disable-next-line:jsx-no-lambda
                    render: routeComponentProps => (React.createElement(RepositoryGitDataContainer, { repoPath: this.state.repoPath },
                        React.createElement(RepositoryCompareArea, Object.assign({}, routeComponentProps, transferProps)))) }),
                React.createElement(Route, { path: `${repoMatchURL}/-/stats`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                    , 
                    // tslint:disable-next-line:jsx-no-lambda
                    render: routeComponentProps => (React.createElement(RepositoryGitDataContainer, { repoPath: this.state.repoPath },
                        React.createElement(RepositoryStatsArea, Object.assign({}, routeComponentProps, transferProps)))) }),
                React.createElement(Route, { path: `${repoMatchURL}/-/settings`, key: "hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
                    , 
                    // tslint:disable-next-line:jsx-no-lambda
                    render: routeComponentProps => (React.createElement(RepoSettingsArea, Object.assign({}, routeComponentProps, transferProps, { onDidUpdateRepository: this.onDidUpdateRepository }))) }),
                React.createElement(Route, { key: "hardcoded-key", component: RepoPageNotFound }))) : (React.createElement(RepositoryErrorPage, { repo: this.state.repoOrError.name, repoID: this.state.repoOrError.id, error: "disabled", viewerCanAdminister: viewerCanAdminister, onDidUpdateRepository: this.onDidUpdateRepository }))));
    }
}
/**
 * Parses the URL path (without the leading slash).
 *
 * TODO(sqs): replace with parseBrowserRepoURL?
 *
 * @param repoRevAndRest a string like /my/repo@myrev/-/blob/my/file.txt
 */
function parseURLPath(repoRevAndRest) {
    const [repoRev, rest] = repoRevAndRest.split('/-/', 2);
    return Object.assign({}, parseRepoRev(repoRev), { rest });
}
//# sourceMappingURL=RepoContainer.js.map