'use strict';
import fetch from 'cross-fetch';
import querystring from 'querystring';
import { pagination, api, aggregate } from './util.js';
import wikiPage from './page.js';
import QueryChain from './chain.js';
/**
 * @namespace
 * @constant
 * @property {string} apiUrl - URL of Wikipedia API
 * @property {string} headers - Headers to pass through to the API request
 * @property {string} origin - When accessing the API using a cross-domain AJAX
 * request (CORS), set this to the originating domain. This must be included in
 * any pre-flight request, and therefore must be part of the request URI (not
 * the POST body). This must match one of the origins in the Origin header
 * exactly, so it has to be set to something like https://en.wikipedia.org or
 * https://meta.wikimedia.org. If this parameter does not match the Origin
 * header, a 403 response will be returned. If this parameter matches the Origin
 * header and the origin is whitelisted, an Access-Control-Allow-Origin header
 * will be set.
 */
const defaultOptions = {
    apiUrl: 'http://en.wikipedia.org/w/api.php',
    origin: '*'
};
/**
 * wiki
 * @example
 * wiki({ apiUrl: 'http://fr.wikipedia.org/w/api.php' }).search(...);
 * @namespace Wiki
 * @param  {Object} options
 * @return {Object} - wiki (for chaining methods)
 */
export default function wiki(options = {}) {
    if (this instanceof wiki) {
        // eslint-disable-next-line
        console.log('Please do not use wikijs ^1.0.0 as a class. Please see the new README.');
    }
    const apiOptions = Object.assign({}, defaultOptions, options);
    function handleRedirect(res) {
        if (res.query.redirects && res.query.redirects.length === 1) {
            return api(apiOptions, {
                prop: 'info|pageprops',
                inprop: 'url',
                ppprop: 'disambiguation',
                titles: res.query.redirects[0].to
            });
        }
        return res;
    }
    /**
     * Search articles
     * @example
     * wiki.search('star wars').then(data => console.log(data.results.length));
     * @example
     * wiki.search('star wars').then(data => {
     * 	data.next().then(...);
     * });
     * @method Wiki#search
     * @param  {string} query - keyword query
     * @param  {Number} [limit] - limits the number of results
     * @param  {Boolean} [all] - returns entire article objects instead of just titles
     * @return {Promise} - pagination promise with results and next page function
     */
    function search(query, limit = 50, all = false) {
        return pagination(apiOptions, {
            list: 'search',
            srsearch: query,
            srlimit: limit
        }, res => res.query.search.map(article => {
            return all ? article : article.title;
        })).catch(err => {
            if (err.message === '"text" search is disabled.') {
                // Try backup search method
                return opensearch(query, limit);
            }
            throw err;
        });
    }
    /**
     * Search articles using "fuzzy" prefixsearch
     * @example
     * wiki.prefixSearch('star wars').then(data => console.log(data.results.length));
     * @example
     * wiki.prefixSearch('star wars').then(data => {
     * 	data.next().then(...);
     * });
     * @method Wiki#prefixSearch
     * @param  {string} query - keyword query
     * @param  {Number} [limit] - limits the number of results
     * @return {Promise} - pagination promise with results and next page function
     */
    function prefixSearch(query, limit = 50) {
        return pagination(apiOptions, {
            list: 'prefixsearch',
            pslimit: limit,
            psprofile: 'fuzzy',
            pssearch: query
        }, res => res.query.prefixsearch.map(article => article.title));
    }
    /**
     * Opensearch (mainly used as a backup to normal text search)
     * @param  {string} query - keyword query
     * @param  {Number} limit - limits the number of results
     * @return {Array}       List of page title results
     */
    function opensearch(query, limit = 50) {
        return api(apiOptions, {
            search: query,
            limit,
            namespace: 0,
            action: 'opensearch',
            redirects: undefined
        }).then(res => res[1]);
    }
    /**
     * Random articles
     * @example
     * wiki.random(3).then(results => console.log(results[0]));
     * @method Wiki#random
     * @param  {Number} [limit] - limits the number of random articles
     * @return {Promise} - List of page titles
     */
    function random(limit = 1) {
        return api(apiOptions, {
            list: 'random',
            rnnamespace: 0,
            rnlimit: limit
        }).then(res => res.query.random.map(article => article.title));
    }
    /**
     * Get Page
     * @example
     * wiki.page('Batman').then(page => console.log(page.pageid));
     * @method Wiki#page
     * @param  {string} title - title of article
     * @return {Promise}
     */
    function page(title) {
        return api(apiOptions, {
            prop: 'info|pageprops',
            inprop: 'url',
            ppprop: 'disambiguation',
            titles: title
        })
            .then(handleRedirect)
            .then(res => {
            const id = Object.keys(res.query.pages)[0];
            if (!id || id === '-1') {
                throw new Error('No article found');
            }
            return wikiPage(res.query.pages[id], apiOptions);
        });
    }
    /**
     * Get Page by PageId
     * @example
     * wiki.findById(4335).then(page => console.log(page.title));
     * @method Wiki#findById
     * @param {integer} pageid, id of the page
     * @return {Promise}
     */
    function findById(pageid) {
        return api(apiOptions, {
            prop: 'info|pageprops',
            inprop: 'url',
            ppprop: 'disambiguation',
            pageids: pageid
        })
            .then(handleRedirect)
            .then(res => {
            const id = Object.keys(res.query.pages)[0];
            if (!id || id === '-1') {
                throw new Error('No article found');
            }
            return wikiPage(res.query.pages[id], apiOptions);
        });
    }
    /**
     * Find page by query and optional predicate
     * @example
     * wiki.find('luke skywalker').then(page => console.log(page.title));
     * @method Wiki#find
     * @param {string} search query
     * @param {function} [predicate] - testing function for choosing which page result to fetch. Default is first result.
     * @return {Promise}
     */
    function find(query, predicate = results => results[0]) {
        return search(query)
            .then(res => predicate(res.results))
            .then(name => page(name));
    }
    /**
     * Geographical Search
     * @example
     * wiki.geoSearch(32.329, -96.136).then(titles => console.log(titles.length));
     * @method Wiki#geoSearch
     * @param  {Number} lat - latitude
     * @param  {Number} lon - longitude
     * @param  {Number} [radius=1000] - search radius in meters (default: 1km)
     * @param  {Number} [limit=10] - number of results (default: 10 results)
     * @return {Promise} - List of page titles
     */
    function geoSearch(lat, lon, radius = 1000, limit = 10) {
        return api(apiOptions, {
            list: 'geosearch',
            gsradius: radius,
            gscoord: `${lat}|${lon}`,
            gslimit: limit
        }).then(res => res.query.geosearch.map(article => article.title));
    }
    /**
     * @summary Find the most viewed pages with counts
     * @example
     * wiki.mostViewed().then(list => console.log(`${list[0].title}: ${list[0].count}`))
     * @method Wiki#mostViewed
     * @returns {Promise} - Array of {title,count}
     */
    function mostViewed() {
        return api(apiOptions, {
            list: 'mostviewed'
        }).then(res => {
            return res.query.mostviewed.map(({ title, count }) => ({ title, count }));
        });
    }
    /**
     * Fetch all page titles in wiki
     * @method Wiki#allPages
     * @return {Array} Array of pages
     */
    function allPages() {
        return aggregate(apiOptions, {}, 'allpages', 'title', 'ap');
    }
    /**
     * Fetch all categories in wiki
     * @method Wiki#allCategories
     * @return {Array} Array of categories
     */
    function allCategories() {
        return aggregate(apiOptions, {}, 'allcategories', '*', 'ac');
    }
    /**
     * Fetch all pages in category
     * @method Wiki#pagesInCategory
     * @param  {String} category Category to fetch from
     * @return {Array} Array of pages
     */
    function pagesInCategory(category) {
        return aggregate(apiOptions, {
            cmtitle: category
        }, 'categorymembers', 'title', 'cm');
    }
    /**
     * @summary Helper function to query API directly
     * @method Wiki#api
     * @param {Object} params [https://www.mediawiki.org/wiki/API:Query](https://www.mediawiki.org/wiki/API:Query)
     * @returns {Promise} Query Response
     * @example
     * wiki().api({
     *	action: 'parse',
     *	page: 'Pet_door'
     * }).then(res => res.parse.title.should.equal('Pet door'));
     */
    function rawApi(params) {
        return api(apiOptions, params);
    }
    /**
     * @summary Returns a QueryChain to efficiently query specific data
     * @method Wiki#chain
     * @returns {QueryChain}
     * @example
     * // Find summaries and images of places near a specific location
     * wiki()
     *	.chain()
     *	.geosearch(52.52437, 13.41053)
     *	.summary()
     *	.image()
     *	.coordinates()
     *	.request()
     */
    function chain() {
        return new QueryChain(apiOptions);
    }
    /**
     * @summary Returns the Export XML for a page to be used for importing into another MediaWiki
     * @method Wiki#exportXml
     * @param {string} pageName
     * @returns {Promise<string>} Export XML
     */
    function exportXml(pageName) {
        const qs = {
            title: 'Special:Export',
            pages: pageName
        };
        // The replace here is kinda hacky since
        // the export action does not use
        // the normal api endpoint.
        const url = `${apiOptions.apiUrl.replace('api', 'index')}?${querystring.stringify(qs)}`;
        const headers = Object.assign({ 'User-Agent': 'WikiJS Bot v1.0' }, apiOptions.headers);
        return fetch(url, { headers }).then(res => res.text());
    }
    return {
        search,
        random,
        page,
        geoSearch,
        options,
        findById,
        find,
        allPages,
        allCategories,
        pagesInCategory,
        opensearch,
        prefixSearch,
        mostViewed,
        api: rawApi,
        chain,
        exportXml
    };
}
