#!/usr/bin/env python3.6

import argparse
import base64
import contextlib
import io
import os
import os.path
import re
import sys
import time

import requests

try:
    import ujson as json
except ImportError:
    import json


xmpp_cache_loc = os.path.join(os.path.dirname(__file__), 'data/hipchat_xmpp_cache.json')


class Exit(Exception):
    pass


def bold(text):
    return f"\033[1m{text}\033[0m"


class TooBig(ValueError):
    ...


def stream_image(img_response):
    MAX_LENGTH = 1 * 1000 * 1000
    CHUNK_READ_SIZE = 1024  # arbitrary afaict

    data = io.BytesIO()
    content_length = img_response.headers.get('Content-Length')
    if content_length and int(content_length) > MAX_LENGTH:
        raise TooBig("Too big")

    size = 0
    for chunk in img_response.iter_content(CHUNK_READ_SIZE):
        size += len(chunk)
        if size > MAX_LENGTH:
            raise TooBig("Too big")

        data.write(chunk)
    return data


def imgcat(data):
    osc = b'\033]'
    st = b'\a'

    buf = bytes()

    buf += osc
    dims = 'width=auto;height=auto'

    buf += b'1337;File=name='
    buf += base64.b64encode(b"this file's name")
    buf += f';size={len(data)};inline=1;{dims}:'.encode('utf8')
    buf += base64.b64encode(data) + st

    if hasattr(sys.stdout, 'buffer'):
        sys.stdout.buffer.write(buf)
    else:
        sys.stdout.write(buf)
    sys.stdout.flush()

    print()


def get_api(path, hipchat_org, token):
    response = requests.get(
        f'https://{hipchat_org}.hipchat.com/{path}',
        headers={'Authorization': f'Bearer {token}'},
    )
    if response.status_code == 401:
        print("Authentication error! Make sure you are using a valid HIPCHAT_TOKEN.")
        raise Exit
    response.raise_for_status()
    return response.json()


def get_by_xmpp_jid(xmpp_lookup, xmpp_jid):
    return xmpp_lookup.get(xmpp_jid)


def get_xmpp_cache(hipchat_org, token):
    try:
        with open(xmpp_cache_loc, 'r') as f:
            return json.load(f)['data']
    except (OSError, ValueError):
        return refresh_xmpp_cache(hipchat_org, token)


def save_xmpp_cache(cache):
    print(xmpp_cache_loc)
    if not os.path.exists(os.path.dirname(xmpp_cache_loc)):
        os.makedirs(os.path.dirname(xmpp_cache_loc))
    with open(xmpp_cache_loc, 'w') as f:
        json.dump({'data': cache, 'timestamp': int(time.time())}, f)


def refresh_xmpp_cache(hipchat_org, token):
    print("Fetching xmpp data...")
    payload = get_api(
        path='v2/room?expand=items&max-results=1000',
        hipchat_org=hipchat_org,
        token=token,
    )
    xmpp_lookup = {
        item['xmpp_jid']: {
            'type': 'room',
            'topic': item['topic'],
            'privacy': item['privacy'],
            'name': item['name'],
            'owner': item['owner'],
            'created': item['created'],
            'id': item['id'],
            'is_archived': item['is_archived'],
            'last_active': item['last_active'],
        }
        for item in payload['items']
    }

    payload = get_api(
        path='v2/user?expand=items&max-results=1000',
        hipchat_org=hipchat_org,
        token=token,
    )
    xmpp_lookup.update({
        item['xmpp_jid']: {
            'type': 'user',
            'name': item['name'],
            'mention_name': item['mention_name'],
            'id': item['id'],
        }
        for item in payload['items']
    })
    save_xmpp_cache(xmpp_lookup)
    return xmpp_lookup


def get_room_data(args, hipchat_org, token):
    payload = get_api(
        path=f'v2/room/{args.room_name}',
        hipchat_org=hipchat_org,
        token=token,
    )

    print("Id:      ", payload['id'])
    print("Name:    ", payload['name'])
    print("Owner:   ", payload['owner']['name'])
    print("Created: ", payload['created'])


def get_unread(args, hipchat_org, token):
    del args
    payload = get_api(
        path='v2/readstate?expand=items.unreadCount',
        hipchat_org=hipchat_org,
        token=token,
    )
    unread = (
        item for item in payload['items']
        if item.get('unreadCount', {}).get('count')
    )

    cache = get_xmpp_cache(hipchat_org=hipchat_org, token=token)

    for state in unread:
        convo = cache.get(state['xmppJid'])

        if not convo:
            print(state)
            continue

        mid = state['mid']
        payload = get_api(
            path=f"v2/{convo['type']}/{convo['id']}/history/latest?not-before={mid}",
            hipchat_org=hipchat_org,
            token=token,
        )
        messages = []
        for message in payload['items']:
            # don't report on cards
            if message.get('card'):
                continue
            sender = message['from']
            if isinstance(sender, dict):
                sender = sender['name']
            messages.append(f"{sender}: {message['message']}")

        if messages:
            print(bold(convo['name']))
            for message in messages:
                print(message)
            print('\n')


def get_emoticon(args, hipchat_org, token):
    payload = get_api(
        path=f'v2/emoticon/{args.emoticon_name}',
        hipchat_org=hipchat_org,
        token=token,
    )

    with contextlib.closing(requests.get(payload['url'], stream=True)) as img_response:
        last_modified = img_response.headers.get('Last-Modified')
        data = stream_image(img_response).getvalue()

    # very rough heuristic, but it's all we've got: if the url contains
    # an organization id it is custom, otherwise it is built-in.
    # Emoticons are only supposed to contain alphanumeric characters
    is_custom = bool(re.search(r'/emoticons/(\d+)/', payload['url']))
    creator = (payload.get('creator') or {}).get('name') or '< unknown >'

    imgcat(data)
    print("Url:     ", payload['url'])
    print("Creator: ", creator if is_custom else f"{creator} (built-in)"),
    print("Added on:", last_modified or 'unknown')


def main():
    token = os.environ.get('HIPCHAT_TOKEN')
    hipchat_org = os.environ.get('HIPCHAT_ORG')
    if not token or not hipchat_org:
        print("Please make sure both HIPCHAT_TOKEN and HIPCHAT_ORG environment variables are set.")
        return

    parser = argparse.ArgumentParser(description="view hipchat data")

    subparsers = parser.add_subparsers(dest='cmd')
    subparsers.required = True

    room_parser = subparsers.add_parser('room', help="view room data")
    room_parser.add_argument('room_name', type=str)
    room_parser.set_defaults(func=get_room_data)

    unread_parser = subparsers.add_parser('unread', help="get unread messages")
    unread_parser.set_defaults(func=get_unread)

    emoticon_parser = subparsers.add_parser('emoticon', help="show emoticon")
    emoticon_parser.add_argument('emoticon_name', type=str)
    emoticon_parser.set_defaults(func=get_emoticon)

    args = parser.parse_args()
    try:
        args.func(args, hipchat_org=hipchat_org, token=token)
    except Exit:
        pass


if __name__ == '__main__':
    main()
