import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import ReactTable from 'react-table';
import escapeRegExp from 'lodash/escapeRegExp';
import endsWith from 'lodash/endsWith';
import { Trans, withNamespaces } from 'react-i18next';
import { HashLink as Link } from 'react-router-hash-link';

import {
    formatTime,
    formatDateTime,
} from '../../helpers/helpers';
import { SERVICES, FILTERED_STATUS, TABLE_DEFAULT_PAGE_SIZE } from '../../helpers/constants';
import { getTrackerData } from '../../helpers/trackers/trackers';
import { formatClientCell } from '../../helpers/formatClientCell';

import Filters from './Filters';
import PageTitle from '../ui/PageTitle';
import Card from '../ui/Card';
import Loading from '../ui/Loading';
import PopoverFiltered from '../ui/PopoverFilter';
import Popover from '../ui/Popover';
import './Logs.css';

const TABLE_FIRST_PAGE = 0;
const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, TABLE_DEFAULT_PAGE_SIZE];
const FILTERED_REASON = 'Filtered';

class Logs extends Component {
    componentDidMount() {
        this.props.setLogsPage(TABLE_FIRST_PAGE);
        this.getLogs(...INITIAL_REQUEST_DATA);
        this.props.getFilteringStatus();
        this.props.getLogsConfig();
    }

    getLogs = (older_than, page) => {
        if (this.props.queryLogs.enabled) {
            this.props.getLogs({
                older_than, page, pageSize: TABLE_DEFAULT_PAGE_SIZE,
            });
        }
    };

    refreshLogs = () => {
        window.location.reload();
    };

    renderTooltip = (isFiltered, rule, filter, service) =>
        isFiltered && <PopoverFiltered rule={rule} filter={filter} service={service} />;

    renderResponseList = (response, status) => {
        if (response.length > 0) {
            const listItems = response.map((response, index) => (
                <li key={index} title={response} className="logs__list-item">
                    {response}
                </li>
            ));

            return <ul className="list-unstyled">{listItems}</ul>;
        }

        return (
            <div>
                <Trans values={{ value: status }}>query_log_response_status</Trans>
            </div>
        );
    };

    toggleBlocking = (type, domain) => {
        const { userRules } = this.props.filtering;
        const { t } = this.props;
        const lineEnding = !endsWith(userRules, '\n') ? '\n' : '';
        const baseRule = `||${domain}^$important`;
        const baseUnblocking = `@@${baseRule}`;
        const blockingRule = type === 'block' ? baseUnblocking : baseRule;
        const unblockingRule = type === 'block' ? baseRule : baseUnblocking;
        const preparedBlockingRule = new RegExp(`(^|\n)${escapeRegExp(blockingRule)}($|\n)`);
        const preparedUnblockingRule = new RegExp(`(^|\n)${escapeRegExp(unblockingRule)}($|\n)`);

        if (userRules.match(preparedBlockingRule)) {
            this.props.setRules(userRules.replace(`${blockingRule}`, ''));
            this.props.addSuccessToast(`${t('rule_removed_from_custom_filtering_toast')}: ${blockingRule}`);
        } else if (!userRules.match(preparedUnblockingRule)) {
            this.props.setRules(`${userRules}${lineEnding}${unblockingRule}\n`);
            this.props.addSuccessToast(`${t('rule_added_to_custom_filtering_toast')}: ${unblockingRule}`);
        }

        this.props.getFilteringStatus();
    };

    renderBlockingButton(isFiltered, domain) {
        const buttonClass = isFiltered ? 'btn-outline-secondary' : 'btn-outline-danger';
        const buttonText = isFiltered ? 'unblock_btn' : 'block_btn';
        const buttonType = isFiltered ? 'unblock' : 'block';

        return (
            <div className="logs__action">
                <button
                    type="button"
                    className={`btn btn-sm ${buttonClass}`}
                    onClick={() => this.toggleBlocking(buttonType, domain)}
                    disabled={this.props.filtering.processingRules}
                >
                    <Trans>{buttonText}</Trans>
                </button>
            </div>
        );
    }

    checkFiltered = reason => reason.indexOf(FILTERED_REASON) === 0;

    checkRewrite = reason => reason === FILTERED_STATUS.REWRITE;

    checkWhiteList = reason => reason === FILTERED_STATUS.NOT_FILTERED_WHITE_LIST;

    getTimeCell = ({ value }) => (
        <div className="logs__row">
            <span className="logs__text" title={formatDateTime(value)}>
                {formatTime(value)}
            </span>
        </div>
    );

    getDomainCell = (row) => {
        const response = row.value;
        const trackerData = getTrackerData(response);

        return (
            <div className="logs__row" title={response}>
                <div className="logs__text">{response}</div>
                {trackerData && <Popover data={trackerData} />}
            </div>
        );
    };

    normalizeResponse = response => (
        response.map((response) => {
            const { value, type, ttl } = response;
            return `${type}: ${value} (ttl=${ttl})`;
        })
    );

    getResponseCell = ({ value: responses, original }) => {
        const {
            reason, filterId, rule, status, originalAnswer,
        } = original;
        const { t, filtering } = this.props;
        const { filters } = filtering;

        const isFiltered = this.checkFiltered(reason);
        const filterKey = reason.replace(FILTERED_REASON, '');
        const parsedFilteredReason = t('query_log_filtered', { filter: filterKey });
        const isRewrite = this.checkRewrite(reason);
        const isWhiteList = this.checkWhiteList(reason);
        const isBlockedService = reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
        const currentService = SERVICES.find(service => service.id === original.serviceName);
        const serviceName = currentService && currentService.name;
        const normalizedAnswer = originalAnswer && this.normalizeResponse(originalAnswer);
        let filterName = '';

        if (filterId === 0) {
            filterName = t('custom_filter_rules');
        } else {
            const filterItem = Object.keys(filters).filter(key => filters[key].id === filterId)[0];

            if (typeof filterItem !== 'undefined' && typeof filters[filterItem] !== 'undefined') {
                filterName = filters[filterItem].name;
            }

            if (!filterName) {
                filterName = t('unknown_filter', { filterId });
            }
        }

        return (
            <div className="logs__row logs__row--column">
                <div className="logs__text-wrap">
                    {originalAnswer && (
                        <span className="logs__text">
                            <Trans>blocked_by_response</Trans>
                        </span>
                    )}
                    {!originalAnswer && (isFiltered || isBlockedService) && (
                        <span className="logs__text" title={parsedFilteredReason}>
                            {parsedFilteredReason}
                        </span>
                    )}
                    {isBlockedService
                        ? this.renderTooltip(isFiltered, '', '', serviceName)
                        : this.renderTooltip(isFiltered, rule, filterName)}
                    {isRewrite && (
                        <strong>
                            <Trans>rewrite_applied</Trans>
                        </strong>
                    )}
                </div>
                <div className="logs__list-wrap">
                    {originalAnswer
                        ? this.renderResponseList(normalizedAnswer, status)
                        : this.renderResponseList(responses, status)
                    }
                    {isWhiteList && this.renderTooltip(isWhiteList, rule, filterName)}
                </div>
            </div>
        );
    };

    getClientCell = (row) => {
        const { original } = row;
        const { t } = this.props;
        const { reason, domain } = original;
        const isFiltered = this.checkFiltered(reason);
        const isRewrite = this.checkRewrite(reason);

        return (
            <Fragment>
                <div className="logs__row logs__row--overflow logs__row--column">
                    {formatClientCell(row, t)}
                </div>
                {isRewrite ? (
                    <div className="logs__action">
                        <Link to="/dns#rewrites" className="btn btn-sm btn-outline-primary">
                            <Trans>configure</Trans>
                        </Link>
                    </div>
                ) : (
                    this.renderBlockingButton(isFiltered, domain)
                )}
            </Fragment>
        );
    };

    fetchData = (state) => {
        const { pages } = state;
        const { oldest, page } = this.props.queryLogs;
        const isLastPage = pages && (page + 1 === pages);

        if (isLastPage) {
            this.getLogs(oldest, page);
        }
    };

    changePage = (page) => {
        this.props.setLogsPage(page);
        this.props.setLogsPagination({ page, pageSize: TABLE_DEFAULT_PAGE_SIZE });
    };

    renderLogs() {
        const { queryLogs, t } = this.props;
        const {
            processingGetLogs, processingGetConfig, logs, pages, page,
        } = queryLogs;
        const isLoading = processingGetLogs || processingGetConfig;

        const columns = [
            {
                Header: t('time_table_header'),
                accessor: 'time',
                maxWidth: 100,
                Cell: this.getTimeCell,
            },
            {
                Header: t('domain_name_table_header'),
                accessor: 'domain',
                minWidth: 180,
                Cell: this.getDomainCell,
            },
            {
                Header: t('type_table_header'),
                accessor: 'type',
                maxWidth: 60,
            },
            {
                Header: t('response_table_header'),
                accessor: 'response',
                minWidth: 250,
                Cell: this.getResponseCell,
            },
            {
                Header: t('client_table_header'),
                accessor: 'client',
                maxWidth: 240,
                minWidth: 240,
                Cell: this.getClientCell,
            },
        ];

        return (
            <ReactTable
                manual
                minRows={5}
                page={page}
                pages={pages}
                columns={columns}
                filterable={false}
                sortable={false}
                data={logs || []}
                loading={isLoading}
                showPagination={true}
                showPaginationTop={true}
                showPageJump={false}
                showPageSizeOptions={false}
                onFetchData={this.fetchData}
                onPageChange={this.changePage}
                className="logs__table"
                defaultPageSize={TABLE_DEFAULT_PAGE_SIZE}
                previousText={t('previous_btn')}
                nextText={t('next_btn')}
                loadingText={t('loading_table_status')}
                rowsText={t('rows_table_footer_text')}
                noDataText={t('no_logs_found')}
                pageText={''}
                ofText={''}
                renderTotalPagesCount={() => false}
                defaultFilterMethod={(filter, row) => {
                    const id = filter.pivotId || filter.id;
                    return row[id] !== undefined
                        ? String(row[id]).indexOf(filter.value) !== -1
                        : true;
                }}
                defaultSorted={[
                    {
                        id: 'time',
                        desc: true,
                    },
                ]}
                getTrProps={(_state, rowInfo) => {
                    if (!rowInfo) {
                        return {};
                    }

                    const { reason } = rowInfo.original;

                    if (this.checkFiltered(reason)) {
                        return {
                            className: 'red',
                        };
                    } else if (this.checkWhiteList(reason)) {
                        return {
                            className: 'green',
                        };
                    } else if (this.checkRewrite(reason)) {
                        return {
                            className: 'blue',
                        };
                    }

                    return {
                        className: '',
                    };
                }}
            />
        );
    }

    render() {
        const { queryLogs, t } = this.props;
        const {
            enabled, processingGetConfig, processingAdditionalLogs, processingGetLogs,
        } = queryLogs;

        const refreshButton = enabled ? (
            <button
                type="button"
                className="btn btn-icon btn-outline-primary btn-sm ml-3"
                onClick={this.refreshLogs}
            >
                <svg className="icons">
                    <use xlinkHref="#refresh" />
                </svg>
            </button>
        ) : (
            ''
        );

        return (
            <Fragment>
                <PageTitle title={t('query_log')}>{refreshButton}</PageTitle>
                {enabled && processingGetConfig && <Loading />}
                {enabled && !processingGetConfig && (
                    <Fragment>
                        <Filters
                            filter={queryLogs.filter}
                            processingGetLogs={processingGetLogs}
                            processingAdditionalLogs={processingAdditionalLogs}
                            setLogsFilter={this.props.setLogsFilter}
                        />
                        <Card>{this.renderLogs()}</Card>
                    </Fragment>
                )}
                {!enabled && !processingGetConfig && (
                    <Card>
                        <div className="lead text-center py-6">
                            <Trans
                                components={[
                                    <Link to="/settings#logs-config" key="0">
                                        link
                                    </Link>,
                                ]}
                            >
                                query_log_disabled
                            </Trans>
                        </div>
                    </Card>
                )}
            </Fragment>
        );
    }
}

Logs.propTypes = {
    getLogs: PropTypes.func.isRequired,
    queryLogs: PropTypes.object.isRequired,
    dashboard: PropTypes.object.isRequired,
    getFilteringStatus: PropTypes.func.isRequired,
    filtering: PropTypes.object.isRequired,
    setRules: PropTypes.func.isRequired,
    addSuccessToast: PropTypes.func.isRequired,
    getClients: PropTypes.func.isRequired,
    getLogsConfig: PropTypes.func.isRequired,
    setLogsPagination: PropTypes.func.isRequired,
    setLogsFilter: PropTypes.func.isRequired,
    setLogsPage: PropTypes.func.isRequired,
    t: PropTypes.func.isRequired,
};

export default withNamespaces()(Logs);
