# Copyright (C) 2017 Minglangjun Li <liminglangjun@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from builtins import range

from .log import logger
from .utils import _sl
from .errors import ValidationError


# FIXME replace it with find_and_modify
def get_or_create_one(db, collection, data, filter=None):
    """Finds and returns a document. If the specified document does not
    exist, this function will insert one.
    """

    if filter is None:
        filter = data
    doc = db[collection].find_one(filter)

    if doc is None:
        # FIXME avoid calling find by using `inserted_id' as `_id'?
        db[collection].insert_one(data)
        return db[collection].find_one(filter), False
    else:
        return doc, True


def get_collection_ids(db, collections):
    """Returns ids of collections."""

    collection_ids = []
    for c in collections:
        name = _sl(c)
        collection_ids.append(get_or_create_one(db, 'collections',
                              {'name': name, 'description': ''},
                              filter={'name': name})[0]['_id'])
    return collection_ids


def add_to_collections(db, records, collections):
    """Adds records to collections."""

    collection_ids = get_collection_ids(db, collections)
    for r in records:
        r['collections'] = collection_ids


def get_author_ids(db, authors):
    """Returns ids of authors."""

    author_ids = []
    for name in authors:
        last, firsts = name.split(',', maxsplit=1)
        last = last.strip()
        firsts = firsts.strip()
        author_ids.append(get_or_create_one(db, 'authors',
                          {'last': last, 'firsts': firsts})[0]['_id'])
    return author_ids


def insert_records(db, records):
    """Inserts records into the database."""

    for r in records:
        if 'author' in r:
            author_ids = get_author_ids(db, r['author'])
            r.pop('author')
            r['authors'] = author_ids
        # FIXME also $unset when bs.keep_custom_fields becomes false?
        db['references'].update_one({'cite_key': r['cite_key']},
                                    {'$set': r}, upsert=True)


def update_collection(db, oldname, name, description):
    """Updates the information of a collection. If `name` is not specified
    and the colleciton does not exists, this function creates it first.
    """

    if (name is None or not _sl(name)) and description is None:
        return
    dic = {}
    if name is not None and _sl(name):
        dic['name'] = _sl(name)
    if description:
        dic['description'] = description
    rsp = db['collections'].update_one({'name': oldname}, {'$set': dic})
    if rsp.matched_count < 1:
        # create a new collection with `description' as its description
        if name is None or not _sl(name):
            dic['name'] = _sl(oldname)
            db['collections'].insert_one(dic)
        else:
            raise ValidationError("collection '{}' does not exist".format(oldname))


def delete_collections(db, collections):
    """Delete collections."""

    for coll in collections:
        # skip non-existing collecitons
        db['collections'].find_one_and_delete({'name': _sl(coll)})


def populate_authors(db, records):
    for idx in range(len(records)):
        r = records[idx]
        if 'authors' in r and r['authors']:
            authors = [db['authors'].find_one({'_id': author_id})
                       for author_id in r['authors']]
            r['authors'] = [', '.join([a['last'], a['firsts']])
                            for a in authors]


def populate_collections(db, records):
    for idx in range(len(records)):
        r = records[idx]
        if 'collections' in r and r['collections']:
            collections = [db['collections'].find_one({'_id': collection_id})
                           for collection_id in r['collections']]
            r['collections'] = [c['name'] for c in collections]
