"""Sum numbers using the Knative Python new()/handle() template."""

import json
import logging
import math


def new():
    return Function()


class Function:
    async def handle(self, scope, receive, send):
        if scope["method"] != "POST":
            await self.respond(send, 405, {"error": "Send an HTTP POST request."})
            return

        body = bytearray()
        while True:
            message = await receive()
            if message["type"] == "http.disconnect":
                return
            body.extend(message.get("body", b""))
            if len(body) > 65536:
                await self.respond(send, 413, {"error": "Request body is too large."})
                return
            if not message.get("more_body", False):
                break

        try:
            data = json.loads(body)
            numbers = data.get("numbers") if isinstance(data, dict) else None
            if not isinstance(numbers, list):
                raise ValueError
            if any(
                type(value) not in (int, float)
                or (isinstance(value, float) and not math.isfinite(value))
                for value in numbers
            ):
                raise ValueError
            total = sum(numbers)
            if isinstance(total, float) and not math.isfinite(total):
                raise ValueError
        except (ValueError, UnicodeError, OverflowError):
            await self.respond(
                send, 400, {"error": 'Send JSON such as {"numbers": [10, 20, 30]}.'}
            )
            return

        await self.respond(send, 200, {"total": total})

    def start(self, cfg):
        logging.info("Sum-numbers function starting")

    def stop(self):
        logging.info("Sum-numbers function stopping")

    def alive(self):
        return True, "Alive"

    def ready(self):
        return True, "Ready"

    @staticmethod
    async def respond(send, status, result):
        body = json.dumps(result, allow_nan=False).encode("utf-8")
        await send(
            {
                "type": "http.response.start",
                "status": status,
                "headers": [
                    (b"content-type", b"application/json"),
                    (b"content-length", str(len(body)).encode("ascii")),
                ],
            }
        )
        await send({"type": "http.response.body", "body": body})
