const _ = require('lodash');
const { URL, URLSearchParams } = require('url');
const fetch = require('node-fetch');
const log = require('../common/log');
const getDetails = require('./getDetails');

const SORT_ORDER_MAP = {
    creation_date: 'id',
    users: 'users_count',
    departments: 'departments_count',
    groups: 'groups_count'
};

const DEFAULT_SORT_ORDER = '-creation_date';

function getSortOrder(req, res) {
    let order = {
        original: req.query.sort || DEFAULT_SORT_ORDER
    };

    order.ascending = SORT_ORDER_MAP[order.original.replace(/^\-/, '')];

    if (!order.ascending) {
        order.original = DEFAULT_SORT_ORDER;
        order.ascending = SORT_ORDER_MAP[order.original.replace(/^\-/, '')];
    }

    order.direction = order.original.charAt(0) === '-' ? -1 : 1;
    order.descending = order.ascending.split(',').map(item => '-' + item).join(',');

    req.context.sort_orders = {};

    Object.keys(SORT_ORDER_MAP).forEach(key => {
        let value = order.original === key ? '-' + key : key;

        req.context.sort_orders[key] = {
            value,
            direction: value.charAt(0) === '-' ? -1 : 1
        };
    });

    return order[order.direction === 1 ? 'ascending' : 'descending'];
}

const SearchType = {
    OWNER_ID: 'owner_id',
    ORGANIZATION: 'organization',
    INTERNAL_ADMIN: 'internal_admin',
    EXTERNAL_ADMIN: 'outer_admin'
};

const CompoundSearchSets = {
    full: [
        SearchType.ORGANIZATION,
        SearchType.INTERNAL_ADMIN,
        SearchType.EXTERNAL_ADMIN
    ],
    admin: [
        SearchType.INTERNAL_ADMIN,
        SearchType.EXTERNAL_ADMIN
    ]
};

function getList(req, res, options = {}) {
    let { query, type } = Object.assign({}, req.query, options);

    if (query) {
        query = query.trim();
    }

    if (query && !type) {
        type = 'full';
    }

    let compoundSearchSet = type && CompoundSearchSets[type];

    if (compoundSearchSet) {
        let optionsSet = compoundSearchSet.map(type => ({ type, query }));

        return getAggregateList(req, res, optionsSet);
    }

    let requestUrl = new URL(`${req.context.config.api.directory}/admin/organizations/`);

    let requestQuery = {
        page: req.params.page,
        ordering: getSortOrder(req, res),
        subscription_plan: req.params.plan
    };

    if (type === SearchType.OWNER_ID) {
        requestQuery[type] = query;
    } else {
        Object.assign(requestQuery, {
            type,
            text: query
        });
    }

    Object.entries(requestQuery).forEach(([key, value]) => {
        if (value !== undefined) {
            requestUrl.searchParams.append(key, value);
        }
    });

    let requestOptions = {
        method: 'get',
        headers: Object.assign({ 'X-Debug': 1 }, req.context.headers)
    };

    return log(fetch)(requestUrl.href, requestOptions)
        .then(response => response.json())
        .catch(error => {});
}

function getAggregateList(req, res, optionsSet) {
    let chain = Promise.resolve();
    let orgSearch = _.find(optionsSet, { type: SearchType.ORGANIZATION });

    // если в списке поисков есть поиск по организации и текст запроса похож на id,
    // для ускорения поиска сначала проверим организацию с таким id
    if (orgSearch && /^\d+$/.test(orgSearch.query)) {
        chain = chain.then(() => {
            return getDetails(req, res, { orgId: orgSearch.query })
                .then(response => {
                    if (response.code === 'not_found') {
                        return;
                    }

                    return { page: 1, pages: 1, total: 1, result: [ response ] };
                })
                .catch(() => {});
        });
    }

    return chain.then(data => {
        if (data) {
            return data;
        }

        let requests = optionsSet.map(options => {
            return getList(req, res, options)
                .then(data => Object.assign(
                    data || {},
                    { _search_type: options.type }
                ));
        });

        return Promise.all(requests).then(response => {
            let output = {
                result: [], total: 0, multishard: false,
                per_page: 0, page: 0, pages: 0
            };

            (response || []).filter(Boolean).forEach((item, k) => {
                output.multishard |= item.multishard;

                output.result = output.result.concat(
                    (item.result || []).map(subitem => {
                        subitem._search_type = item._search_type;
                        return subitem;
                    })
                );

                output.total += item.total;
                output.per_page = item.per_page;
                output.page = item.page;

                if (item.pages > 1) {
                    output.pages += item.pages;
                } else {
                    output.pages = Math.max(output.pages, item.pages);
                }
            });

            output.multishard = Boolean(output.multishard);
            output.result = _.uniqBy(output.result, 'id').slice(0, output.per_page);

            return output;
        });
    });
}

module.exports = getList;
