#!/usr/bin/env python3

import getopt
import http.cookiejar
import sys
import urllib.parse
import urllib.request
from http.cookies import SimpleCookie
from json import loads as json_loads
from os import environ

_headers = {"Referer": 'https://rentry.co'}


class UrllibClient:
    """Simple HTTP Session Client, keeps cookies."""

    def __init__(self):
        self.cookie_jar = http.cookiejar.CookieJar()
        self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cookie_jar))
        urllib.request.install_opener(self.opener)

    def get(self, url, headers={}):
        request = urllib.request.Request(url, headers=headers)
        return self._request(request)

    def post(self, url, data=None, headers={}):
        postdata = urllib.parse.urlencode(data).encode()
        request = urllib.request.Request(url, postdata, headers)
        return self._request(request)

    def _request(self, request):
        response = self.opener.open(request)
        response.status_code = response.getcode()
        response.data = response.read().decode('utf-8')
        return response


def raw(url):
    client = UrllibClient()
    return json_loads(client.get('https://rentry.co/api/raw/{}'.format(url)).data)


def new(url, edit_code, text):
    client, cookie = UrllibClient(), SimpleCookie()

    cookie.load(vars(client.get('https://rentry.co'))['headers']['Set-Cookie'])
    csrftoken = cookie['csrftoken'].value

    payload = {
        'csrfmiddlewaretoken': csrftoken,
        'url': url,
        'edit_code': edit_code,
        'text': text
    }

    return json_loads(client.post('https://rentry.co/api/new', payload, headers=_headers).data)


def edit(url, edit_code, text):
    client, cookie = UrllibClient(), SimpleCookie()

    cookie.load(vars(client.get('https://rentry.co'))['headers']['Set-Cookie'])
    csrftoken = cookie['csrftoken'].value

    payload = {
        'csrfmiddlewaretoken': csrftoken,
        'edit_code': edit_code,
        'text': text
    }

    return json_loads(client.post('https://rentry.co/api/edit/{}'.format(url), payload, headers=_headers).data)


def usage():
    print('''
Usage: rentry {new | edit | raw} {-h | --help} {-u | --url} {-p | --edit-code} text

Commands:
  new   create a new entry
  edit  edit an existing entry
  raw   get raw markdown text of an existing entry
    
Options:
  -h, --help                 show this help message and exit
  -u, --url URL              url for the entry, random if not specified
  -p, --edit-code EDIT-CODE  edit code for the entry, random if not specified
    
Examples:
  rentry new 'markdown text'               # new entry with random url and edit code
  rentry new -p pw -u example 'text'       # with custom edit code and url 
  rentry edit -p pw -u example 'text'      # edit the example entry
  cat file | rentry new                    # read from pipe and paste it to rentry
  rentry raw -u example                    # get raw markdown text
  rentry raw -u https://rentry.co/example  # -u accepts absolute and relative urls
    ''')


if __name__ == '__main__':
    try:
        environ.pop('POSIXLY_CORRECT', None)
        opts, args = getopt.gnu_getopt(sys.argv[1:], "hu:p:", ["help", "url=", "edit-code="])
    except getopt.GetoptError as e:
        sys.exit("error: {}".format(e))

    command, url, edit_code, text = None, '', '', None

    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        elif o in ("-u", "--url"):
            url = urllib.parse.urlparse(a).path.strip('/')
        elif o in ("-p", "--edit-code"):
            edit_code = a

    command = (args[0:1] or [None])[0]
    command or sys.exit(usage())
    command in ['new', 'edit', 'raw'] or sys.exit('error: command must be new, edit or raw')

    text = (args[1:2] or [None])[0]
    if not text and command != 'raw':
        text = sys.stdin.read().strip()
        text or sys.exit('error: text is required')

    if command == 'new':
        response = new(url, edit_code, text)
        if response['status'] != '200':
            print('error: {}'.format(response['content']))
            try:
                for i in response['errors'].split('.'):
                    i and print(i)
                sys.exit(1)
            except:
                sys.exit(1)
        else:
            print('Url:        {}\nEdit code:  {}'.format(response['url'], response['edit_code']))

    elif command == 'edit':
        url or sys.exit('error: url is required')
        edit_code or sys.exit('error: edit code is required')

        response = edit(url, edit_code, text)
        if response['status'] != '200':
            print('error: {}'.format(response['content']))
            try:
                for i in response['errors'].split('.'):
                    i and print(i)
                sys.exit(1)
            except:
                sys.exit(1)
        else:
            print('Ok')

    elif command == 'raw':
        url or sys.exit('error: url is required')
        response = raw(url)
        if response['status'] != '200':
            sys.exit('error: {}'.format(response['content']))
        print(response['content'])
