#!/usr/bin/env python3
"""Stateless Adapter business callback: validates configuration; creates no external resources."""
import hmac
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import os


def evaluate(body):
    if (set(body) != {'contractVersion', 'context', 'configuration', 'resources'}
            or body['contractVersion'] != '1.0.0'):
        raise ValueError('Invalid business contract')
    context = body['context']
    if (context['stage'] not in {'BEFORE_RESOURCES', 'AFTER_RESOURCES'}
            or context['operation']['operationType'] not in {'INSTALL', 'UPGRADE', 'CONFIGURE', 'DETACH'}
            or type(context['executionSequence']) is not int or context['executionSequence'] <= 0):
        raise ValueError('Invalid operation')
    configuration = body['configuration']
    if (configuration['schemaVersion'] != '1.0.0'
            or set(configuration['values']) != {'message'}
            or not isinstance(configuration['values']['message'], str)
            or not configuration['values']['message'].strip()):
        raise ValueError('message must be non-empty')
    # No business state or side effects: both stages verify the same configuration.
    return {'contractVersion': '1.0.0', 'outcome': 'VERIFIED', 'resources': {}}


class Handler(BaseHTTPRequestHandler):
    business_token = ''

    def log_message(self, *_):
        pass  # Never log callback bodies or credentials.

    def do_POST(self):
        code, data = '0', None
        supplied = self.headers.get('Authorization', '')
        if (self.path != '/execute' or not self.business_token
                or not hmac.compare_digest(supplied.encode(), ('Bearer ' + self.business_token).encode())):
            code, data = 'BUSINESS_UNAUTHORIZED', {'message': 'Unauthorized business callback'}
        else:
            try:
                size = int(self.headers['Content-Length'])
                if not 0 < size <= 1024 * 1024:
                    raise ValueError('Invalid body size')
                data = evaluate(json.loads(self.rfile.read(size)))
            except (ValueError, KeyError, TypeError):
                code, data = 'BUSINESS_INVALID_CONFIGURATION', {'message': 'Invalid callback or configuration'}
        encoded = json.dumps({'contractVersion': '1.0.0', 'errorCode': code, 'data': data}).encode()
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Cache-Control', 'no-store')
        self.send_header('Content-Length', str(len(encoded)))
        self.end_headers()
        self.wfile.write(encoded)


if __name__ == '__main__':
    Handler.business_token = os.environ['ADAPTER_BUSINESS_TOKEN']
    if not Handler.business_token or not Handler.business_token.isascii():
        raise SystemExit('Provide a non-empty ASCII business credential')
    port = int(os.environ['APP_ENDPOINT_LIFECYCLE_PORT'])
    HTTPServer(('127.0.0.1', port), Handler).serve_forever()
