import _ from 'lodash';
import ApplicationStore from 'stores/Application';
import UserStore from 'stores/Users';
import AuthStore from 'stores/Auth';
import ConfigStore from 'stores/Config';
import history from 'services/history';

const SETUP_PATH = 'setup';
const UTM_PREFIX = 'utm_';

const Actions = {
    ADD: 'add',
    ADD_USER: 'add-user',
    ADD_GROUP: 'add-group',
    ADD_DOMAIN: 'add-domain',
    INVITE_USER: 'invite-user',
    ADD_SUBSCRIPTION: 'add-subscription',
};

const Path = {
    ADMINISTRATION: 'admin',
    DASHBOARD: 'home',
    STAFF: 'staff',
    FORBIDDEN: 'forbidden',
};

const group = {
    normalizeSubsection(subsection) {
        if (subsection === 'auto') {
            subsection = ApplicationStore.getSubsectionName();
        }
        if (subsection === 'all') {
            subsection = null;
        }

        return subsection;
    },
};

const Url = {

    Actions,

    Path,

    /**
     * Возвращает url, построенный по шаблону
     * @method  build
     * @param   {String}  template  шаблон
     * @param   {Object|Array}  data  данные для подстановки в шаблон
     * @returns {String}
     */
    build(template, data) {
        return template.replace(/\$\{([^\{\}]+)\}/g, (match, name) => encodeURIComponent(data[name]));
    },

    getRoot() {
        const appRoot = ConfigStore.get('app.root');
        let path = location.pathname || '';

        if (path.indexOf(appRoot) === 0) {
            path = path.substring(appRoot.length);
        }

        return appRoot + (path.split('/').filter(Boolean)[0] || '');
    },

    getContext() {
        const appRoot = ConfigStore.get('app.root');
        let root = this.getRoot();

        if (root.indexOf(appRoot) === 0) {
            root = root.substring(appRoot.length);
        }

        return root.replace(/(^\/|\/$)/g, '');
    },

    /**
     * Возвращает текущий путь
     * @returns {String}
     */
    getCurrentPath() {
        return location.pathname || '';
    },

    /**
     * Возвращает путь в контексте текущего раздела `/admin` или `/staff`
     * @method  getContextPath
     * @param   {...String}  parts  относительный путь или его компоненты
     * @returns {String}
     */
    getContextPath(...parts) {
        const appRoot = ConfigStore.get('app.root');
        let context = this.getContext();

        if (['admin', 'staff'].indexOf(context) === -1) {
            context = 'staff';
        }

        // ('a', 'b') => '{app_root}/{admin|staff}/a/b'
        const pathComponents = parts
            .map(part => part ? String(part).replace(/^\//, '') : '')
            .filter(Boolean);

        pathComponents.unshift(appRoot + context);

        return pathComponents.join('/');
    },

    /**
     * Возвращает путь от корня приложения
     * @method  getPath
     * @param   {...String}  parts  относительный путь или его компоненты
     * @returns {String}
     */
    getPath(...parts) {
        // ('a', 'b') => '{app_root}/a/b'
        const pathComponents = parts
            .map(part => part ? String(part).replace(/^\//, '') : '')
            .filter(Boolean);

        return ConfigStore.get('app.root') +
            pathComponents.join('/').replace('/#', '#');
    },

    /**
     * Возвращает путь страницы пользователя
     * @method  getUserPath
     * @param   {String}  id
     * @returns {String}
     */
    getUserPath(id) {
        return this.getContextPath('users', id);
    },

    /**
     * Возвращает путь страницы админа организации
     * @method  getOrgAdminPath
     * @param   {String}  id
     * @returns {String}
     */
    getOrgAdminPath(id) {
        return this.getContextPath('customization', 'admins', id);
    },

    /**
     * Возвращает путь страницы отдела
     * @method  getDepartmentPath
     * @param   {String}  id
     * @returns {String}
     */
    getDepartmentPath(id) {
        return this.getContextPath('departments', id);
    },

    /**
     * Возвращает путь страницы команды
     * @method  getGroupPath
     * @param   {String}  subsection   auto | all | admin | member
     * @param   {String}  id
     * @returns {String}
     */
    getGroupPath(subsection, id) {
        return this.getContextPath('groups', group.normalizeSubsection(subsection), id);
    },

    /**
     * Возвращает путь страницы родительской команды текущего объекта
     * @method  getParentGroupPath
     * @returns {String}
     */
    getParentGroupPath() {
        const sections = ApplicationStore.getSections();
        const groupSection = _.find(sections, section => _.get(section, 'object.type') === 'group');

        return this.getGroupPath('auto', _.get(groupSection, 'object.id'));
    },

    /**
     * Возвращает путь страницы со списком команд
     * @method  getGroupListPath
     * @param   {String}  subsection
     * @returns {String}
     */
    getGroupListPath(subsection) {
        return this.getGroupPath(subsection);
    },

    /**
     * Возвращает путь страницы пользователя внутри команды
     * @method  getGroupMemberPath
     * @param   {String}  subsection
     * @param   {String}  groupId
     * @param   {Object}  groupMember  { id, type, role? }
     * @returns {String}
     */
    getGroupMemberPath(subsection, groupId, groupMember) {
        if (groupMember.type === 'user' && AuthStore.getViewMode() === 'staff') {
            return UserStore.getUrl(groupMember.id);
        }

        return this.getContextPath(
            'groups', group.normalizeSubsection(subsection), groupId,
            groupMember.role, `${groupMember.type}s`, groupMember.id
        );
    },

    /**
     * Возвращает путь страницы организации
     * @method  getOrganizationPath
     * @param   {String}  orgId
     * @param   {String}  retpath
     * @returns {String}
     */
    getOrganizationPath(orgId, retpath) {
        const { sk } = window.ya.connect.initial;
        const url = encodeURIComponent(
            retpath ? this.getLocation(retpath) : location.href
        );

        return this.getPath(`/context?org_id=${orgId}&mode=portal&retpath=${url}&sk=${sk}`);
    },

    /**
     * Возвращает путь страницы организации
     * @method  getNewOrganizationUrl
     * @param   {String}  id
     * @returns {String}
     */
    getNewOrganizationUrl(id) {
        const appRoot = ConfigStore.get('app.root');

        return this.getLocation(`${appRoot}home/?org-id=${id}`);
    },

    /**
     * Возвращает абсолютный путь дашборда
     * @method   getDashboardUrl
     * @returns {String}
     */
    getDashboardUrl() {
        const appRoot = ConfigStore.get('app.root');

        return this.getLocation(`${appRoot}home`);
    },

    /**
     * Возвращает ссылку на страницу сервиса
     * @method   getServiceSettingsUrl
     * @param   {String}  [serviceSlug]
     * @returns {String}
     */
    getServiceSettingsUrl(serviceSlug) {
        return this.getPath('services', serviceSlug);
    },

    /**
     * Возвращает абсолютный путь до онбоардинга
     * @method  getOnboargingUrl
     * @returns {String}
     */
    getOnboargingUrl() {
        const appRoot = ConfigStore.get('app.root');

        return this.getLocation(`${appRoot}start/`);
    },

    /**
     * Возвращает путь к странице домена
     * @method  getDomainSettingsPath
     * @param   {String}  domainName
     * @returns {String}
     */
    getDomainSettingsPath(domainName) {
        return this.getContextPath('domains', domainName);
    },

    /**
     * Проверяет, совпадает ли указанный путь с текущим
     * @method  isCurrentPath
     * @param   {String}  path
     * @returns {Boolean}
     */
    isCurrentPath(path) {
        const currentPath = location.href.replace(`${location.protocol}//${location.host}`, '');

        return path === currentPath;
    },

    /**
     * Проверяет, ведет ли текущий путь на дорегистрации
     * @returns {Boolean}
     */
    isSetupPath() {
        return location.pathname.indexOf(this.getPath(SETUP_PATH)) === 0;
    },

    /**
     * Возвращает декодированный retpath
     * @method  getReturnPath
     * @param   {String} [url]
     * @returns {String}
     */
    getReturnPath(url) {
        let query;

        if (url) {
            query = url.split('?')[1] || '';
        } else {
            query = location.search.substring(1);
        }

        const returnPath = (query.match(/(^|&)retpath=([^&]+)/) || [])[2];

        if (returnPath) {
            return decodeURIComponent(returnPath);
        }
    },

    /**
     * Возвращает значение GET-параметра из текущего адреса
     * @param {String} name - название параметра
     * @returns {String}
     */
    getQueryParam(name) {
        const query = `&${location.search.substring(1)}`;
        const part = query.split(`&${name}=`)[1];

        return part ? decodeURIComponent(part.split('&')[0]) : '';
    },

    /**
     * Отдает строку с параметрами из адреса страницы
     * @param {String} [pageId]
     * @returns {String}
     */
    getSourceParams(pageId) {
        const query = location.search.substring(1);
        const sources = query.split('&').filter(item => item.indexOf(UTM_PREFIX) === 0);

        const originSource = this.getQueryParam('source');

        if (originSource) {
            sources.push(`source=${originSource}`);
        }

        if (pageId) {
            sources.push(`origin=${pageId}`);
        }

        return sources.join('|');
    },

    /**
     * Возвращает ссылку на несуществующую страницу
     * @returns {*|String}
     */
    getUnknownPath() {
        return this.getPath('unknown');
    },

    getEmail(email) {
        return this.build(ConfigStore.get('ui.email'), { email });
    },

    /**
     * Добавляет к url query-параметры, передаваемые в формы, встроеннные в страницу
     *
     * @param {String} url
     * @param {Object} formData
     * @param {String?|Number?} formId
     * @returns {String}
     * @see https://st.yandex-team.ru/LOCDOC-732
     *
     * ('https://ya.ru/help', { x: 1 }) > 'https://ya.ru/help?form-x=1'
     * ('https://ya.ru/help', { x: 1 }, 321) > 'https://ya.ru/help?form321-x=1'
     */
    setFormOptions(url, formData = {}, formId) {
        /* нужно разобраться с полифиллом для URL в IE
        const nextUrl = new URL(url);
        const nextQuery = new URLSearchParams(nextUrl.search);

        Object.keys(formData).forEach(key => {
            if (formData[key] !== null && formData[key] !== undefined) {
                nextQuery.set(`form${formId || ''}-${key}`, formData[key]);
            }
        });

        nextUrl.search = nextQuery.toString();

        return nextUrl.href;
        */

        if (!url) {
            return url;
        }

        const parts = url.match(/^([^\?#]+)?(\?[^#]+)?(#.*)?$/)
            .map(part => part || '');

        Object.keys(formData).forEach(key => {
            if (formData[key] !== null && formData[key] !== undefined) {
                const formKey = encodeURIComponent(`form${formId || ''}-${key}`);
                const formValue = encodeURIComponent(formData[key]);

                parts[2] += `${parts[2] ? '&' : '?'}${formKey}=${formValue}`;
            }
        });

        return parts.slice(1).join('');
    },

    /**
     * Устанавливает начальное действие для UI в хэш урла
     * @param {String} url
     * @param {String} action
     * @returns {String}
     */
    setInitialAction(url, action) {
        if (url.indexOf('#') !== -1) {
            url = url.replace(/#.*$/, '');
        }

        return url + (action ? `#${action}` : '');
    },

    /**
     * Возвращает начальное действие для UI из хэша урла
     * @param {String?} url
     * @returns {String?}
     */
    getInitialAction(url) {
        if (!url) {
            url = location.href;
        }

        let action;
        const actionCandidate = (url.match(/#(.*)$/) || [])[1];

        _.forEach(this.Actions, listedAction => {
            if (listedAction === actionCandidate) {
                action = actionCandidate;
            }
        });

        return action;
    },

    /**
     * Переходит по заданному адресу без записи в history
     * @param {String} url
     */
    replace(url) {
        history.replace(url);
    },

    /**
     * Возвращает хост
     * @returns {String}
     */
    getHostName() {
        return location.hostname;
    },

    /**
     * Возвращает хост c портом
     * @returns {String}
     */
    getHost() {
        return location.host;
    },

    /**
     * Возвращает полный урл
     * @param {String?} path
     * @returns {String}
     */
    getLocation(path) {
        if (path === undefined) {
            path = this.getCurrentPath();
        }

        if (/^(https?:)?\/\//.test(path)) {
            return path;
        }

        path = path ? (path.indexOf('/') === 0 ? '' : '/') + path : '';

        return [location.protocol, '//', location.host, path].join('');
    },

    /**
     * Возвращает GET-параметры из урла текущей страницы
     * @returns {String}
     */
    getQuery() {
        return location.search.substring(1);
    },

    isSafe(x) {
        return !/^\s*javascript:/i.test(x);
    },

    // защита от редиректов на произвольные адреса
    // https://wiki.yandex-team.ru/clickdaemon/
    getSafeRedirectUrl(url) {
        return url ? `http://clck.yandex.ru/redir/dtype=stred/*data=url%3D${encodeURIComponent(url)}` : '';
    },

    /**
     * Переходит по заданному адресу
     * @method  open
     * @param   {String}    path
     * @param   {String?}   target
     */
    open(path, target) {
        if (path.indexOf('#') === 0) {
            path = location.href.replace(/#.*$/, '') + path;
        }

        const isExternalUrl = /^(https?:)?\/\//.test(path);

        if (isExternalUrl || target) {
            if (target) {
                const win = window.open(path, target);

                win.opener = null;

                return;
            }

            location.href = path;
        } else {
            history.push(path);
        }
    },

    /**
     * Проверяет валидность origin из postMessage
     * @method isAllowedIframeOrigin
     * @param {String} origin
     * @returns {Boolean}
     */
    isAllowedIframeOrigin(origin) {
        const { hostname } = new URL(origin);
        const allowedIframeOriginHosts = ConfigStore.get('frameAncestors').toJS();

        if (hostname === this.getHostName()) {
            return true;
        }

        return _.some(allowedIframeOriginHosts, item => {
            if (item === '*') {
                return true;
            }

            if (item.indexOf('*.') === 0) {
                let itemBase = item.substring(2); // без '*.'

                // true, если hostname заканчивается на itemBase
                return hostname.slice(-itemBase.length) === itemBase;
            }

            return item === hostname;
        });
    },
};

export default window.ya.connect.Url = Url;
