#!/usr/bin/env python3
"""DeviceGuide Edge HTTP teaching receiver. No device commands; no persistence."""
import argparse
import base64
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlsplit


def describe(packet):
    if packet == {}:
        return {'kind': 'test-empty-object'}
    if not isinstance(packet, dict):
        raise ValueError('expected JSON object')
    if 'app' in packet:
        app = packet['app']
        user = app['userdata']
        eui, port, payload = app['moteeui'], user['port'], user['payload']
        kind = 'native-uplink'
    else:
        eui, port, payload = packet['devEUI'], packet['fPort'], packet['data']
        kind = 'compat-uplink'
    if not isinstance(eui, str) or not isinstance(port, int) or isinstance(port, bool):
        raise ValueError('invalid EUI/port types')
    raw = base64.b64decode(payload, validate=True)
    return {'kind': kind, 'devEUI': eui, 'fPort': port, 'hex': raw.hex(' ')}


class Receiver(BaseHTTPRequestHandler):
    def reply(self, status, result):
        body = json.dumps(result, ensure_ascii=False).encode('utf-8')
        self.send_response(status)
        self.send_header('Content-Type', 'application/json; charset=utf-8')
        self.send_header('Content-Length', str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def do_POST(self):
        if urlsplit(self.path).path != '/uplink':
            self.reply(404, {'error': 'use /uplink'})
            return
        try:
            length = int(self.headers.get('Content-Length', '0'))
            if not 0 < length <= 1024 * 1024:
                raise ValueError('expected body of 1..1048576 bytes')
            packet = json.loads(self.rfile.read(length))
            result = describe(packet)
        except (ValueError, KeyError, TypeError) as error:
            self.reply(400, {'error': str(error)})
            return
        print(json.dumps({'path': self.path, 'result': result, 'packet': packet}, ensure_ascii=False), flush=True)
        self.reply(200, {'accepted': True, **result})


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--bind', default='127.0.0.1')
    parser.add_argument('--port', type=int, default=8088)
    args = parser.parse_args()
    print(f'Teaching receiver: http://{args.bind}:{args.port}/uplink', flush=True)
    ThreadingHTTPServer((args.bind, args.port), Receiver).serve_forever()
