#!/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 requests_html
import urllib


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))

    for node in meaning_divs:
        yield node.text


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("UrbanDictionary results for {!r}".format(word_to_search))

    def normalize(text):
        # NOTE unescape seems do not work as text missing `;`, e.g `&apos;` ->
        # `&apos`, thus, use dirty replace
        return html.unescape(text).replace("&apos", "'")

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

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

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


def _test():
    import doctest

    doctest.testmod()


if __name__ == "__main__":
    main()
