#!/usr/bin/env python3
# hnplain: output Hacker News in plain text
# -*- coding: utf-8 -*-


import urllib.request
import json
import argparse
import textwrap
import html

from multiprocessing import Pool
from html.parser import HTMLParser
from datetime import datetime

API_URL = "https://hacker-news.firebaseio.com/v0/"


def http_get(url):
    with urllib.request.urlopen(url) as handle:
        content = handle.read()
        return content.decode("utf-8")


def api_call(endpoint):
    text = http_get(API_URL + endpoint + ".json")
    return json.loads(text)


def many_api_call(endpoints):
    pool = Pool(len(endpoints))
    return pool.map(api_call, endpoints)


class MLStripper(HTMLParser):
    def __init__(self):
        super().__init__(convert_charrefs=True)
        self.reset()
        self.fed = []

    def handle_data(self, d):
        self.fed.append(self.unescape(d))

    def get_data(self):
        return " ".join(self.fed)


def strip_tags(input):
    s = MLStripper()
    s.feed(html.unescape(input))
    return s.get_data()


def print_post(n, post):
    title = "{0}. {1}".format(n + 1, post["title"])
    underscore = len(title) * "="
    print(title)
    print(underscore)
    if "url" in post:
        link = "Link: {0}".format(post["url"])
        print(link)
    if post["type"] != "job":
        hnlink = "https://news.ycombinator.com/item?id={0}".format(post["id"])
        comments = "Comments ({0}): {1}".format(post["descendants"], hnlink)
        print(comments)
    if "comment" in post:
        comment = post["comment"]
        stamp = datetime.fromtimestamp(comment["time"])
        stamp = stamp.strftime("%Y-%m-%d %H:%M:%S")
        text = textwrap.wrap(strip_tags(comment["text"]))
        print()
        print("{0} {1}".format(comment["by"], stamp))
        print("\n".join(["> " + line for line in text]))


if __name__ == "__main__":
    endpoints = {
        "top": "topstories",
        "new": "newstories",
        "ask": "askstories",
        "job": "jobstories",
        "show": "showstories",
    }

    parser = argparse.ArgumentParser(description="output Hacker News in plain text")
    parser.add_argument(
        "--list",
        dest="list",
        default="top",
        choices=endpoints.keys(),
        help="what list to pull stories from (default: top)",
    )
    parser.add_argument(
        "-n", dest="n", default=15, type=int, help="the number of stories (default: 30)"
    )
    parser.add_argument(
        "--topcomment",
        action="store_true",
        dest="topcomment",
        help="add the top comment of a story",
    )

    args = parser.parse_args()

    endpoint = endpoints[args.list]

    post_ids = api_call(endpoint)[0 : args.n]
    post_data = many_api_call(["item/" + str(s) for s in post_ids])

    comment_ids = [post["kids"][0] for post in post_data if "kids" in post]
    if args.topcomment and len(comment_ids) > 0:
        comment_data = many_api_call(["item/" + str(s) for s in comment_ids])
        comment_dict = dict([(c["parent"], c) for c in comment_data])
        for post in post_data:
            if post["id"] in comment_dict:
                post["comment"] = comment_dict[post["id"]]

    for post in enumerate(post_data):
        print_post(*post)
        print()
