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

const RecordsRequestOptions = {
    offset: 0,
    // TODO: DIR-7589 Добавить пагинацию на страницу с DNS-записями в каталоге
    size: 5000
};

function getRecords(req, domain, orgId) {
    if (!domain) {
        return;
    }

    const prepare = getDomain(req, domain, orgId)
        // если домена нет в днс-хостинге, пытаемся включить его;
        // это опционально, поэтому промис prepare резолвим при любом исходе;
        // если домен не удастся включить, запрос к records всё равно
        // даст актуальную картину
        .then(({ code }) => {
            if (code === 'domain_not_found') {
                return enableDomain(req, domain, orgId).catch(() => {});
            }
        });

    return prepare
        .then(() => {
            const url = new URL(`${req.context.config.api.dns}/domains/${encodeURIComponent(domain)}./records/`);
            const headers = getHeaders(req, orgId);

            Object.entries(RecordsRequestOptions).forEach(([key, value]) => {
                url.searchParams.append(key, value);
            });

            return log(fetch)(url.href, { headers }).then(res => res.json());
        })
        .then(data => {
            if (data && data.items) {
                data._description = getDescription(data);

                data.items = data.items
                    .map(record => {
                        record._content = getContent(record);
                        record._priority = getPriority(record);
                        return record;
                    })
                    .sort((a, b) => a.type === b.type ? 0 : (a.type < b.type ? -1 : 1));
            }

            return data;
        });
}

function getDescription(data) {
    const index = {};

    data.items.forEach(({ type }) => {
        index[type] = (index[type] || 0) + 1;
    });

    return Object.entries(index)
        .map(([key, value]) => `${value} ${key}`)
        .join(', ');
}

function enableDomain(req, domain, orgId) {
    const url = `${req.context.config.api.dns}/domains/`;
    const options = {
        method: 'POST',
        headers: getHeaders(req, orgId),
        body: JSON.stringify({ name: `${domain}.` })
    };

    return log(fetch)(url, options).then(res => res.json());
}

function getDomain(req, domain, orgId) {
    const url = `${req.context.config.api.dns}/domains/${encodeURIComponent(domain)}./`;
    const options = {
        headers: getHeaders(req, orgId)
    };

    return log(fetch)(url, options).then(res => res.json());
}

// передана запись с id - будет обновлена;
// передана запись без id - будет создана новая
function submitRecord(req, domain, recordData, orgId) {
    const url = `${req.context.config.api.dns}/domains/${encodeURIComponent(domain)}./records/`;
    const options = {
        method: 'POST',
        headers: getHeaders(req, orgId),
        body: JSON.stringify(recordData)
    };

    return log(fetch)(url, options).then(res => res.json());
}

function removeRecord(req, domain, recordId, orgId) {
    const url = `${req.context.config.api.dns}/domains/${encodeURIComponent(domain)}./records/${encodeURIComponent(recordId)}/`;
    const options = {
        method: 'DELETE',
        headers: getHeaders(req, orgId)
    };

    return log(fetch)(url, options);
}

function getHeaders(req, orgId) {
    const { tvm_tickets: tvmTickets, headers } = req.context;

    let output = Object.assign({}, headers);

    output['X-Ya-Service-Ticket'] = tvmTickets.dns;

    if (orgId) {
        output['x-org-id'] = orgId;
    }

    delete output['X-Ya-User-Ticket'];

    return output;
}

const ContentMap = {
    A: '${address}',
    AAAA: '${address}',
    SRV: '${ttl} IN SRV ${priority} ${weight} ${port} ${target}',
    MX: '${exchange}',
    CNAME: '${target}',
    NS: '${target}'
};

function getContent(record) {
    const { type, rdata, ttl } = record;

    if (type === 'TXT') {
        return rdata.strings && rdata.strings.join('');
    }

    if (ContentMap[type]) {
        return template.build(ContentMap[type], Object.assign({ ttl }, rdata));
    }

    return record.content;
}

const DefaultPriorityMap = {
    SRV: 10,
    MX: 10
};

function getPriority(record) {
    const { rdata: { priority, preference }, type } = record;

    if (type === 'MX' && preference !== undefined) {
        return preference;
    }

    return priority === undefined ? DefaultPriorityMap[type] : priority;
}

module.exports = {
    getRecords,
    submitRecord,
    removeRecord
};
