"""Utility functions for the plugins table."""

import difflib
import random
import re
import sys

import rethinkdb as r
from slugify import slugify

import db.util

r_conn = db.util.r_conn


class RequiredProperty(object):
    pass


_ROW_SCHEMA = {

    # Primary key. Human-readable permalink for a plugin. Eg. 'python-2'
    'slug': RequiredProperty(),

    # A name used strictly for purposes of associating info from different
    # sources together. Eg. "nerdtree" (instead of "the-NERD-Tree.vim")
    'normalized_name': '',

    # One of the IDs from db/categories.yaml
    # Eg. 'language'
    'category': 'uncategorized',

    # eg. ['C/C++', 'autocomplete']
    'tags': [],

    # Unix timestamp in seconds
    'created_at': 0,
    'updated_at': 0,

    ###########################################################################
    # Info from the script on vim.org.
    # eg. http://www.vim.org/scripts/script.php?script_id=2736

    # Eg. '1234' (string)
    'vimorg_id': '',

    # Eg. 'Syntastic'
    'vimorg_name': '',

    # Eg. 'Marty Grenfell'
    'vimorg_author': '',

    # eg. 'http://www.vim.org/scripts/script.php?script_id=2736'
    'vimorg_url': '',

    # eg. 'utility'
    'vimorg_type': '',

    'vimorg_rating': 0,
    'vimorg_num_raters': 0,
    'vimorg_downloads': 0,
    'vimorg_short_desc': '',
    'vimorg_long_desc': '',
    'vimorg_install_details': '',

    ###########################################################################
    # Info from the author's GitHub repo (eg. github.com/scrooloose/syntastic)

    # The unique identifier of a GitHub repo that's preserved on name changes
    # or owner transfers. eg. '123567'
    'github_repo_id': '',

    # eg. 'scrooloose'
    'github_owner': '',

    # eg. 'syntastic'
    'github_repo_name': '',

    # Eg. 'Martin Grenfell'
    'github_author': '',

    'github_stars': 0,

    # eg. 'Syntax checking hacks for vim'
    'github_short_desc': '',

    # eg. 'http://valloric.github.io/YouCompleteMe/'
    'github_homepage': '',

    'github_readme': '',

    'github_readme_filename': '',

    ###########################################################################
    # Info from the github.com/vim-scripts mirror.
    # eg. github.com/vim-scripts/Syntastic

    # Eg. 'syntastic'
    'github_vim_scripts_repo_name': '',

    'github_vim_scripts_stars': 0,

    ###########################################################################
    # Info derived from elsewhere

    # Number of Vundle/Pathogen/NeoBundle etc. users that reference the
    # author's GitHub repo.
    'github_bundles': 0,

    # Number of Vundle/Pathogen/NeoBundle etc. users that reference the
    # vim-scripts GitHub mirror.
    'github_vim_scripts_bundles': 0,

}


# Reserve some slug names for potential pages in case we want to be able to
# link to plugins top-level, as in vimawesome.com/:slug
_RESERVED_SLUGS = set([
    'plugins',
    'plugin',
    'p',
    'tags',
    'tag',
    't',
    'about',
    'submit',
    'upload',
    'search',
    'faq',
    'blog',
])


###############################################################################
# Routines for basic DB CRUD operations.


_GITHUB_REPO_URL_TEMPLATE = 'https://github.com/%s/%s'


def ensure_table():
    db.util.ensure_table('plugins', primary_key='slug')

    db.util.ensure_index('plugins', 'vimorg_id')
    db.util.ensure_index('plugins', 'github_stars')
    db.util.ensure_index('plugins', 'normalized_name')
    db.util.ensure_index('plugins', 'github_repo_id')
    db.util.ensure_index('plugins', 'github_owner_repo',
            lambda p: [p['github_owner'], p['github_repo_name']])


# TODO(david): Yep, using an ODM enforcing a consistent schema on write AND
#     read would be great.
def insert(plugins, *args, **kwargs):
    """Insert or update a plugin or list of plugins.

    Although this would be more accurately named "upsert", this is a wrapper
    around http://www.rethinkdb.com/api/python/#insert that ensures
    a consistent plugin schema before inserting into DB.
    """
    if not isinstance(plugins, list):
        plugins = [plugins]

    mapped_plugins = []
    for plugin in plugins:
        _normalize(plugin)

        mapped_plugins.append(dict(_ROW_SCHEMA, **plugin))

    return r.table('plugins').insert(mapped_plugins, *args, **kwargs).run(
            r_conn())


def _normalize(plugin):
    if not plugin.get('slug'):
        plugin['slug'] = _generate_unique_slug(plugin)

    if not plugin.get('normalized_name'):
        plugin['normalized_name'] = _normalize_name(plugin)

    # Normalize the GitHub URL properties
    for key in ['github_owner', 'github_repo_name']:
        # Vim.org plugins don't have GitHub info
        if key in plugin:
            plugin[key] = plugin[key].lower()


def _generate_unique_slug(plugin):
    """Create a unique, human-readable ID for this plugin that can be used in
    a permalink URL.

    WARNING: Not thread-safe.
    """
    name = (plugin.get('vimorg_name') or plugin.get('github_repo_name') or
            plugin.get('github_vim_scripts_repo_name'))
    assert name

    slug = slugify(name)
    if not _slug_taken(slug):
        return slug

    # If the slug isn't unique, try appending different slug suffixes until we
    # get a unique slug. Don't worry, these suffixes only show up in the URL.
    # And it's more efficient to randomly permute these than using
    # a monotonically increasing integer.
    #
    # Also this is just wayyyyyyyy more awesome than appending numbers. <3
    slug_suffixes = [
        'say-im-great',
        'already-like-death',
        'he-is-going',
        'may-fear-less',
        'are-made-of',
        'is-written-on',
        'lives-by-it',
        'tree-and-truth',
        'today-cannot-touch',
        'face-rejection',
        'hard-things',
        'please-everybody',
        'with-ourselves',
        'frighten-you',
        'it-has-you',
        'hands-off',
        'thing-itself',
        'the-thing-itself',
        'impatience-and-laziness',
        'be-who-we-are',
        'care-of-itself',
        'would-can-one',
        'left-unsaid',
        'or-are-not',
        'is-holy',
        'the-heights',
        'without-it',
        'own-character',
        'who-speaks',
        'looking-forward',