import React from 'react';
import ButtonGroup from 'react-bootstrap/lib/ButtonGroup';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import _ from 'lodash';
import classnames from 'classnames';

import {Icon} from '../Icon';
import {Select} from '../form/Select';
import {Button} from '../Button';
import {translate} from '../../../utils';

import lang from '../../../lang/common/index-en_US';

const CLASS_NAME_DOTS_HEAD = 'gap-left';
const CLASS_NAME_DOTS_TAIL = 'gap-right';
const CLASS_NAME_PREV_NAV = 'page-prev';
const CLASS_NAME_NEXT_NAV = 'page-next';
const CLASS_NAME_DISABLED = 'disabled';

const PAGING_MODE_LISTING = 'listing';
const PAGING_MODE_PAGES = 'pages';

enum PagingMode {
    PAGING_MODE_LISTING = 'listing',
    PAGING_MODE_PAGES = 'pages'
}

type RangesResult = {
    head: [number, number] | null;
    middle: [number, number] | null;
    tail: [number, number] | null;
};

type PurePaginationProps = {
    className: string;
    showPageSizeSelector: boolean;
    beforeAfterAmount: number;
    boundaryAmount: number;
    mode: PagingMode;
    currentPageLabel: string;
    goToPageLabel: string;
    pageNextLabel: string;
    pagePrevLabel: string;
    pageSizeSelectorText: string;
    allText: string;
    pageLabel: string;
    itemsCount: number;
    activePage: number;
    onClickNext: Function;
    onClickPrev: Function;
    isNextButtonDisabled?: null | Function;
    isPrevButtonDisabled?: null | Function;
    onChangePageSize: Function;
    onChangePage: Function;
    pageSizes: number[];
    pageSize: number;
};

export class PurePagination extends React.Component<PurePaginationProps> {
    static PagingModes = {
        listing: PAGING_MODE_LISTING,
        pages: PAGING_MODE_PAGES
    };

    static defaultProps = {
        pageSizes: [10, 25, 50, 100, 500],
        className: null,
        showPageSizeSelector: true,
        beforeAfterAmount: 2,
        boundaryAmount: 1,
        mode: PAGING_MODE_PAGES,
        onClickNext: _.noop,
        onClickPrev: _.noop,
        get currentPageLabel() {
            return lang.CURRENT_PAGE;
        },
        get goToPageLabel() {
            return lang.GO_TO_PAGE;
        },
        get pageNextLabel() {
            return lang.GO_TO_NEXT_PAGE;
        },
        get pagePrevLabel() {
            return lang.GO_TO_PREV_PAGE;
        },
        get pageSizeSelectorText() {
            return lang.SHOW;
        },
        get allText() {
            return lang.ALL;
        },
        get pageLabel() {
            return ''; //FIXME [UIA-10000]
        }
    };

    calculatePageCount(itemsCount: number, pageSize: number): number {
        return Math.ceil(itemsCount / pageSize);
    }

    /**
     * Calculates pagination ranges
     *
     * @param {Number} activePage Zero based value of active page
     * @param {Number} beforeAfterAmount A number of items wanted to show before and after active page
     * @param {Number} boundaryAmount A number of items wanted to show on head and on tail
     * @param {Number} pageCount Actual count of pages
     */
    getRanges(
        activePage: number,
        beforeAfterAmount: number,
        boundaryAmount: number,
        pageCount: number
    ): RangesResult {
        let headMiddleDistance = activePage - beforeAfterAmount - boundaryAmount;
        let headSize = headMiddleDistance < 0 ? 0 : boundaryAmount;
        headSize = Math.min(headSize, pageCount);

        let lastPage = pageCount - 1;
        let middleFrom = Math.max(0, activePage - beforeAfterAmount);
        let overlappedHead = boundaryAmount - headSize;

        let headFrom = 0;
        let headTo = headFrom + headSize - 1;

        let middleSize = 1 + 2 * beforeAfterAmount;
        middleSize = middleSize + overlappedHead;

        if (middleFrom - headSize <= 1) {
            middleSize = middleSize + 1;
            middleFrom = headSize;
        }

        let middleTo = middleFrom + middleSize - 1;
        let middleTailDistance = lastPage - boundaryAmount - middleTo;

        if (middleTailDistance === 1) {
            middleTo = middleTo + 1;
        } else if (middleTailDistance <= 0) {
            middleFrom = middleFrom - Math.abs(middleTailDistance);
            middleFrom = middleFrom - 1;
        }

        middleFrom = Math.max(middleFrom, headSize);
        middleTo = Math.min(lastPage, middleTo);

        let tailFrom = lastPage - boundaryAmount + 1;
        tailFrom = Math.max(middleTo + 1, tailFrom);
        let tailTo = lastPage;

        let result: RangesResult = {
            head: [headFrom, headTo],
            middle: [middleFrom, middleTo],
            tail: [tailFrom, tailTo]
        };

        result.head = this.isRangeEmpty(result.head) ? null : result.head;
        result.middle = this.isRangeEmpty(result.middle) ? [0, 0] : result.middle;
        result.tail = this.isRangeEmpty(result.tail) ? null : result.tail;

        return result;
    }

    isRangeEmpty(range) {
        if (!range) {
            return true;
        }
        return range[1] < range[0];
    }

    hasGap(rangeA, rangeB) {
        if (this.isRangeEmpty(rangeA) || this.isRangeEmpty(rangeB)) {
            return false;
        }
        return rangeB[0] - rangeA[1] > 1;
    }

    hasHeadDots(ranges) {
        return this.hasGap(ranges.head, ranges.middle);
    }

    hasTailDots(ranges) {
        return this.hasGap(ranges.middle, ranges.tail);
    }

    formatPageSize(digit, itemsCount, pageSizes) {
        // In case of store size less/equal/ to MAX_AVAILABLE_PAGE_SIZE, visually display
        // bigger page size option as 'all'
        const maxPageSize = Math.max(...pageSizes);

        if (digit === maxPageSize && itemsCount <= maxPageSize) {
            return translate(this.props.allText || '');
        }

        return digit;
    }

    getPageSizeSelector(pageSizes, pageSize, itemsCount) {
        let dropdownItems = pageSizes.map((sizeValue, index) => {
            return (
                <option key={index} value={sizeValue} data-test-automation-id={'size' + index}>
                    {this.formatPageSize(sizeValue, itemsCount, pageSizes)}
                </option>
            );
        });

        let controlId = `pageSizeSelector_${+new Date()}`;

        return (
            <Select
                name="page-selector"
                value={pageSize}
                onChange={this.handleChangePageSize}
                className="rc-items-per-page-selector"
                label={this.getPageSizeSelectorLabel()}
                testAutomationId="pageSizeSelector"
                controlId={controlId}>
                {dropdownItems}
            </Select>
        );
    }

    getPageSizeSelectorLabel() {
        return this.props.pageSizeSelectorText;
    }

    getPageCount() {
        const {itemsCount, pageSize} = this.props;

        return this.calculatePageCount(itemsCount, pageSize);
    }

    getPageIndex() {
        return this.props.activePage + 1;
    }

    isNextButtonDisabled() {
        let {isNextButtonDisabled, activePage} = this.props;
        if (isNextButtonDisabled) {
            return isNextButtonDisabled();
        }
        return activePage >= this.getPageCount() - 1;
    }

    isPrevButtonDisabled() {
        let {isPrevButtonDisabled, activePage} = this.props;
        if (typeof isPrevButtonDisabled === 'function') {
            return isPrevButtonDisabled();
        }
        return activePage <= 0;
    }

    handleClickPrev = () => {
        let {activePage, onChangePage, onClickPrev} = this.props;
        let prevPage = Math.max(0, activePage - 1);
        if (prevPage !== activePage) {
            onChangePage(prevPage);
        }
        onClickPrev();
    };

    handleClickNext = () => {
        let {onChangePage, activePage, onClickNext} = this.props;
        onChangePage(activePage + 1);
        onClickNext();
    };

    handleChangePageSize = event => {
        const {value, selectedIndex} = event.target;

        this.props.onChangePageSize(Number(value), selectedIndex);
    };

    renderDot(className) {
        return (
            <Button disabled bsStyle={'link'} className={className}>
                ...
            </Button>
        );
    }

    renderPages(range, activePage, handler) {
        if (this.isRangeEmpty(range)) {
            return [];
        }

        const {goToPageLabel, currentPageLabel} = this.props;

        let list = _.times(range[1] - range[0] + 1, n => {
            let page = range[0] + n;
            let active = activePage === page;
            return (
                <Button
                    data-test-automation-id={'p' + (page + 1)}
                    bsStyle="link"
                    aria-label={`${goToPageLabel} ${page + 1}${
                        active ? ', ' + currentPageLabel : ''
                    }`}
                    data-page={page}
                    onClick={handler.bind(this, page)}
                    key={page}
                    active={active}
                    disabled={active}>
                    {page + 1}
                </Button>
            );
        });

        return list;
    }

    render() {
        let {
            mode,
            pageNextLabel,
            pagePrevLabel,
            activePage,
            onChangePage,
            pageSizes,
            pageSize,
            itemsCount,
            beforeAfterAmount,
            boundaryAmount
        } = this.props;

        let ranges = this.getRanges(
            activePage,
            beforeAfterAmount,
            boundaryAmount,
            this.getPageCount()
        );

        const isPrevButtonDisabled = this.isPrevButtonDisabled();
        const isNextButtonDisabled = this.isNextButtonDisabled();

        let isPagesMode = mode === PAGING_MODE_PAGES;

        let prevButtonClasses = classnames(
            CLASS_NAME_PREV_NAV,
            isPrevButtonDisabled && CLASS_NAME_DISABLED
        );
        let prevButton = (
            <Button
                data-test-automation-id="prev"
                bsStyle="link"
                aria-label={pagePrevLabel}
                className={prevButtonClasses}
                disabled={isPrevButtonDisabled}
                onClick={this.handleClickPrev}>
                <Icon name="chevron-left-small" />
            </Button>
        );

        let nextButtonClasses = classnames(
            CLASS_NAME_NEXT_NAV,
            isNextButtonDisabled && CLASS_NAME_DISABLED
        );

        let nextButton = (
            <Button
                data-test-automation-id="next"
                bsStyle="link"
                aria-label={pageNextLabel}
                className={nextButtonClasses}
                disabled={isNextButtonDisabled}
                onClick={this.handleClickNext}>
                <Icon name="chevron-right-small" />
            </Button>
        );

        let head = this.renderPages(ranges.head, activePage, onChangePage);
        let middle = this.renderPages(ranges.middle, activePage, onChangePage);
        let tail = this.renderPages(ranges.tail, activePage, onChangePage);

        let pageSizeSelector;

        if (this.props.showPageSizeSelector) {
            pageSizeSelector = this.getPageSizeSelector(pageSizes, pageSize, itemsCount);
        }

        let cn = classnames('rc-pagination', 'form-inline rc-form-inline', this.props.className);
        return (
            <div aria-label="pagination" className={cn} data-test-automation-id="pages">
                {pageSizeSelector}
                {isPagesMode ? (
                    <span className="form-group">
                        <ButtonGroup bsSize="small">
                            {prevButton}
                            {head}
                            {this.hasHeadDots(ranges) ? this.renderDot(CLASS_NAME_DOTS_HEAD) : null}
                            {middle}
                            {this.hasTailDots(ranges) ? this.renderDot(CLASS_NAME_DOTS_TAIL) : null}
                            {tail}
                            {nextButton}
                        </ButtonGroup>
                    </span>
                ) : (
                    <span className="form-group">
                        <Row>
                            <Col xs={3}>{prevButton}</Col>
                            <Col xs={6} className="text-center">
                                <span data-test-automation-id="label">
                                    {translate(this.props.pageLabel || '')} {this.getPageIndex()}
                                </span>
                            </Col>
                            <Col xs={3}>{nextButton}</Col>
                        </Row>
                    </span>
                )}
            </div>
        );
    }
}
