"""Serve the python console using socket communication."""

import asyncio
import socket
from functools import partial

from . import console


async def handle_connect(reader, writer, factory, banner=None):
    streams = reader, writer
    interface = factory(streams=streams)
    await interface.interact(banner=banner, stop=False, handle_sigint=False)
    writer.close()


async def start_interactive_server(
    factory=console.AsynchronousConsole,
    host=None,
    port=None,
    path=None,
    banner=None,
    *,
    loop=None
):
    if (port is None) == (path is None):
        raise ValueError("Either a TCP port or a UDS path should be provided")
    if port is not None:
        # Override asyncio behavior (i.e serve on all interfaces by default)
        host = host or "localhost"
        start_server = partial(asyncio.start_server, host=host, port=port)
    else:
        start_server = partial(asyncio.start_unix_server, path=path)

    client_connected = partial(handle_connect, factory=factory, banner=banner)
    server = await start_server(client_connected, loop=loop)
    return server


async def start_console_server(
    host=None,
    port=None,
    path=None,
    locals=None,
    filename="<console>",
    banner=None,
    prompt_control=None,
    *,
    loop=None
):
    def factory(streams):
        client_locals = dict(locals) if locals is not None else None
        return console.AsynchronousConsole(
            streams=streams,
            locals=client_locals,
            filename=filename,
            prompt_control=prompt_control,
        )

    server = await start_interactive_server(
        factory, host=host, port=port, path=path, banner=banner, loop=loop
    )
    return server


def print_server(server, name="console", file=None):
    interface = server.sockets[0].getsockname()
    if server.sockets[0].family != socket.AF_UNIX:
        interface = "{}:{}".format(*interface)
    print("The {} is being served on {}".format(name, interface), file=file)


def run(host=None, port=None, path=None):
    loop = asyncio.get_event_loop()
    coro = start_interactive_server(host=host, port=port, path=path)
    loop.server = loop.run_until_complete(coro)
    print_server(loop.server)
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass


def parse_server(server, parser=None):
    try:
        host, port = server.rsplit(":", maxsplit=1)
    except ValueError:
        host, port = "localhost", server
    try:
        port = int(port)
    except (ValueError, TypeError):
        msg = "{!r} is not a valid server [HOST:]PORT".format(server)
        if not parser:
            raise ValueError(msg)
        parser.error(msg)
    return host, port
