import $ from 'jquery';
import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';

import urls from 'bitbucket/internal/impl/urls';

import Avatar from '../aui-react/avatar';
import Icon from '../aui-react/icon';
import FilterBar from '../filter-bar/filter-bar';
import models from '../models/models';
import PullRequestListTable from '../pull-request-list-table/pull-request-list-table';
import openInSameTab from './dom-event';

function renderAuthorItem(selectionOrResult, author, $container) {
    const size = selectionOrResult === 'selection' ? 'xsmall' : 'small';
    ReactDOM.render(
        <Avatar
            person={author}
            size={size}
            withName
            withEmail={selectionOrResult === 'result'}
            className={"pull-request-list-filter filter-" + selectionOrResult}
        />,
        $container[0]);
}

function renderBranchItem(selectionOrResult, branch, $container) {
    ReactDOM.render(
        <span className={"pull-request-list-filter filter-" + selectionOrResult}>
            <Icon size="small" icon="devtools-branch">{AJS.I18n.getText('bitbucket.component.pull.request.list.branch')}</Icon>
            <span className="name" title={branch.display_id}>{branch.display_id}</span>
        </span>,
        $container[0]);
}

const GettingStarted = props => {
    return (
        <div className="pull-request-intro">
            <div className="intro-image">
                {/* an <img /> tag is not used as there is no easy way to determine the URL */}
            </div>
            <div className="intro-text">
                <h3>{AJS.I18n.getText('bitbucket.component.pull.request.list.intro.title')}</h3>
                <p>{AJS.I18n.getText('bitbucket.component.pull.request.list.intro.description')}</p>
            </div>
            <div className="intro-buttons">
                <a id="empty-list-create-pr-button" className="aui-button aui-button-primary" href={urls.createPullRequest(props.repository)}>
                    {AJS.I18n.getText('bitbucket.component.pull.request.list.intro.button.create')}
                </a>
                <a id="empty-list-help-button" className="aui-button aui-button-link help-button" target="_blank" href={urls.help('help.pull.request')}>
                    {AJS.I18n.getText('bitbucket.component.pull.request.list.intro.button.help')}
                </a>
            </div>
        </div>
    );
};
GettingStarted.propTypes = {
    repository: PropTypes.object,
};

const NoResults = props => {
    function resetFilters(e) {
        if ($(e.target).closest('#reset-filters').length && openInSameTab(e)) {
            props.onResetFilters();
            e.preventDefault();
        }
    }
    return (
        <div className="empty-banner-content">
            <h3>{props.filtered ?
                AJS.I18n.getText('bitbucket.component.pull.request.filtered.no.matches') :
                AJS.I18n.getText('bitbucket.component.pull.request.no.open')}</h3>
            <p
                dangerouslySetInnerHTML={{
                    __html: props.filtered ?
                        AJS.I18n.getText('bitbucket.component.pull.request.filtered.no.matches.description',
                            '<a id="reset-filters" href="' + urls.allPullRequests(props.repository) + '">', '</a>') :
                        AJS.I18n.getText('bitbucket.component.pull.request.no.open.description',
                            '<a href="' + urls.createPullRequest(props.repository) + '">', '</a>'),
                }} onClick={resetFilters}
            />
        </div>
    );
};
NoResults.propTypes = {
    filtered: PropTypes.bool.isRequired,
    repository: PropTypes.object,
    onResetFilters: PropTypes.func,
};

export default class PullRequestList extends Component {

    static get propTypes() {
        const handledByUs = [
            'id',
            'label',
        ];
        return {
            allFetched: PropTypes.bool.isRequired,
            currentUser: PropTypes.any,
            initialFilter: PropTypes.shape({
                state: PropTypes.shape(_.omit(FilterBar.Select.propTypes, handledByUs)).isRequired,
                author: PropTypes.shape(_.omit(FilterBar.AsyncSelect.propTypes, handledByUs)).isRequired,
                target_ref: PropTypes.shape(_.omit(FilterBar.AsyncSelect.propTypes, handledByUs)).isRequired,
                reviewer_self: PropTypes.shape(_.omit(FilterBar.Toggle.propTypes, handledByUs)).isRequired,
            }),
            focusedIndex: PropTypes.number,
            gettingStarted: PropTypes.bool,
            loading: PropTypes.bool.isRequired,
            onFilterChange: PropTypes.func.isRequired,
            onMorePrsRequested: PropTypes.func.isRequired,
            pullRequests: PropTypes.array.isRequired,
            repository: PropTypes.any.isRequired,
            selectedAuthor: PropTypes.any,
            selectedTargetBranch: PropTypes.any,
        };
    }

    constructor(...args) {
        super(...args);
        this.onFilterChange = this.onFilterChange.bind(this);
        this.resetFilters = this.resetFilters.bind(this);
    }

    componentWillMount() {
        if (!this.props.pullRequests.length && !this.props.allFetched) {
            this.props.onMorePrsRequested();
        }

        this.setState({
            isFiltered: (!!this.props.initialFilter.state.value && this.props.initialFilter.state.value !== 'OPEN') ||
                !!this.props.initialFilter.author.value ||
                !!this.props.initialFilter.target_ref.value ||
                !!this.props.initialFilter.reviewer_self.value,
        });
    }

    onFilterChange(newState) {
        const cleanState = {
            state: newState['pr-state-filter'],
            author_id: newState['pr-author-filter'] || null,
            target_ref_id: newState['pr-target-branch-filter'] || null,
            reviewer_self: newState['pr-reviewer-self-filter'],
        };
        this.setState({
            isFiltered: cleanState.state !== 'OPEN' ||
                !!cleanState.author_id ||
                !!cleanState.target_ref_id ||
                !!cleanState.reviewer_self,
        }, () => this.props.onFilterChange(cleanState));
    }

    resetFilters() {
        if (this._filterBar) {
            this._filterBar.reset();
        }
    }

    render() {
        const noPrs = !this.props.loading && this.props.pullRequests.length === 0;
        const children = [];
        if (noPrs && this.props.gettingStarted) {
            children.push(<GettingStarted key="getting-started" repository={this.props.repository} />);
        } else {
            children.push(
                <FilterBar key="filter-bar" onChange={this.onFilterChange} ref={el => (this._filterBar = el)}>
                    <FilterBar.Select
                        {...this.props.initialFilter.state}
                        id="pr-state-filter"
                        label={AJS.I18n.getText('bitbucket.component.pull.request.list.state')}
                        menu={{
                            items: [
                                {
                                    id: models.PullRequestState.OPEN,
                                    text: AJS.I18n.getText('bitbucket.component.pull.request.list.state.open'),
                                },
                                {
                                    id: models.PullRequestState.MERGED,
                                    text: AJS.I18n.getText('bitbucket.component.pull.request.list.state.merged'),
                                },
                                {
                                    id: models.PullRequestState.DECLINED,
                                    text: AJS.I18n.getText('bitbucket.component.pull.request.list.state.declined'),
                                },
                            ],
                        }}
                    />
                    <FilterBar.AsyncSelect
                        {...this.props.initialFilter.author}
                        id="pr-author-filter"
                        label={AJS.I18n.getText('bitbucket.component.pull.request.list.author')}
                        searchPlaceholder={AJS.I18n.getText('bitbucket.component.pull.request.list.search.author')}
                        menu={{
                            id: user => user.name,
                            initSelection: ($el, callback) => {
                                const username = $el.val();
                                if (this.props.initialFilter.author.value === username) {
                                    return callback(this.props.selectedAuthor);
                                }
                                throw new Error(`Unexpected value '${username}' when initializing the author filter.`);
                            },
                            formatSelection: renderAuthorItem.bind(null, 'selection'),
                            formatResult: renderAuthorItem.bind(null, 'result'),
                            formatNoMatches: () => {
                                return AJS.I18n.getText('bitbucket.component.pull.request.list.search.author.nomatches');
                            },
                            placeholder: AJS.I18n.getText('bitbucket.component.pull.request.list.author'),
                            dropdownCssClass: 'pr-author-dropdown',
                        }}
                    />
                    <FilterBar.AsyncSelect
                        {...this.props.initialFilter.target_ref}
                        id="pr-target-branch-filter"
                        label={AJS.I18n.getText('bitbucket.component.pull.request.list.branch.target')}
                        searchPlaceholder={AJS.I18n.getText('bitbucket.component.pull.request.list.search.branch')}
                        menu={{
                            initSelection: ($el, callback) => {
                                const branch = $el.val();
                                if (this.props.initialFilter.target_ref.value === branch) {
                                    return callback(this.props.selectedTargetBranch);
                                }
                                throw new Error(`Unexpected value '${branch}' when initializing the target branch filter.`);
                            },
                            formatSelection: renderBranchItem.bind(null, 'selection'),
                            formatResult: renderBranchItem.bind(null, 'result'),
                            formatNoMatches: () => {
                                return AJS.I18n.getText('bitbucket.component.pull.request.list.search.branch.nomatches');
                            },
                            placeholder: AJS.I18n.getText('bitbucket.component.pull.request.list.branch.target'),
                            dropdownCssClass: 'pr-target-branch-dropdown',
                        }}
                    />
                    {this.props.currentUser && (
                        <FilterBar.Toggle
                            {...this.props.initialFilter.reviewer_self}
                            id="pr-reviewer-self-filter"
                            label={AJS.I18n.getText('bitbucket.component.pull.request.list.reviewer.self')}
                        />
                    )}
                </FilterBar>
            );
            children.push(noPrs ?
                <NoResults key="no-results" filtered={this.state.isFiltered} repository={this.props.repository} onResetFilters={this.resetFilters} /> :
                <PullRequestListTable
                    key="table"
                    focusedIndex={this.props.focusedIndex}
                    pullRequests={this.props.pullRequests}
                    allFetched={this.props.allFetched}
                    onMoreItemsRequested={this.props.onMorePrsRequested}
                    loading={this.props.loading}
                />
            );
        }

        return (
            <div className="pull-request-list">
                {children}
            </div>
        );
    }
}
