#!/usr/bin/env python
# vim: ft=python
# TODO support py2 if requests_html supports
__doc__ = "Simple CLI for searching word meanings from UrbanDictionary"

import argparse
import html
import sys
import textwrap
import urllib

import requests_html
import crayons


def colorize(text, color, bold=False):
    """
    https://github.com/kennethreitz/crayons
    Included colors are red, green, yellow, blue, black,
    magenta, cyan, white, and normal
    """
    return eval("str(crayons.{}(text, bold=bold))".format(color))


def get_meanings(word):
    """
    >>> "misleading" in " ".join(get_meanings("red herring"))
    True
    >>> "that feel when" in " ".join(get_meanings("tfw")).lower()
    True
    """
    sess = requests_html.HTMLSession()
    r = sess.get(
        "https://www.urbandictionary.com/define.php?term={}".format(
            urllib.parse.quote(word)
        )
    )
    meaning_divs = r.html.xpath('//div[@class="meaning"]')
    if not meaning_divs:
        if "There are no definitions for this word" in r.html.full_text:
            return []
        else:
            raise Exception("Unknown result for {}".format(word))

    return [node.text for node in meaning_divs]


def main():
    parser = argparse.ArgumentParser(__doc__)
    parser.add_argument(
        "word",
        help="Word to search for meaning on UrbanDictionary",
        type=str,
        nargs="+",
    )
    parser.add_argument(
        "-n",
        "--count",
        help="Number of meanings to display",
        type=int,
        default=8,
    )
    args = parser.parse_args()
    word_to_search = " ".join(args.word)
    print(
        colorize(
            "UrbanDictionary results for {}".format(
                colorize(word_to_search, "yellow", bold=True)
            ),
            "blue",
            bold=True,
        )
    )

    def normalize(text, padding=" " * 6):
        # NOTE unescape seems do not work as text missing `;`, e.g `&apos;` ->
        # `&apos`, thus, use dirty replace
        cleaned = html.unescape(text).replace("&apos", "'")
        cleaned = textwrap.fill(cleaned, width=120)
        cleaned = textwrap.indent(cleaned, prefix=padding).strip()
        return cleaned

    def pad_lines(text, pad=""):
        padded = "\n        ".join(
            (
                "{pad}{line}".format(line=normalize(line), pad=pad)
                for line in text.splitlines()
                if line
            )
        )
        return padded

    meanings = get_meanings(word_to_search)
    if not meanings:
        sys.exit(crayons.red("There are no definitions for this word."))

    for idx, _meaning in enumerate(meanings, start=1):
        result = "{:>2}{idx}.  {meaning}".format(
            "", idx=colorize(idx, "red"), meaning=pad_lines(_meaning)
        )
        print(result)
        if idx == args.count:
            break


def _test():
    import doctest

    doctest.testmod()


if __name__ == "__main__":
    main()
