# This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API.
#
# Copyright (C) 2021 Alban 'spl0k' Féron
#
# Distributed under terms of the GNU AGPLv3 license.

import importlib
import os
import os.path

from click import command, option, Option
from click.exceptions import UsageError, ClickException
from click.types import Choice

from ..web import create_application

_servers = [
    e.name[:-3]
    for e in os.scandir(os.path.dirname(__file__))
    if not e.name.startswith("_") and e.name.endswith(".py")
]


class MutuallyExclusiveOption(Option):
    def __init__(self, *args, **kwargs):
        self.mutually_exclusive = set(kwargs.pop("mutually_exclusive", []))
        help = kwargs.get("help", "")
        if self.mutually_exclusive:
            ex_str = ", ".join(self.mutually_exclusive)
            kwargs[
                "help"
            ] = "{}  NOTE: This argument is mutually exclusive with arguments: [{}].".format(
                help, ex_str
            )
        super().__init__(*args, **kwargs)

    def handle_parse_result(self, ctx, opts, args):
        if self.mutually_exclusive.intersection(opts) and self.name in opts:
            raise UsageError(
                "Illegal usage: `{}` is mutually exclusive with arguments `{}`.".format(
                    self.name, ", ".join(self.mutually_exclusive)
                )
            )

        return super().handle_parse_result(ctx, opts, args)


def get_server(name):
    return importlib.import_module("." + name, __package__).server


def find_first_available_server():
    for module in _servers:
        try:
            return get_server(module)
        except ImportError:
            pass

    return None


@command()
@option(
    "-S",
    "--server",
    type=Choice(_servers),
    help="Specify which WSGI server to use. Pick the first available if none is set",
)
@option(
    "-h",
    "--host",
    default="0.0.0.0",
    show_default=True,
    help="Hostname or IP address on which to listen",
    cls=MutuallyExclusiveOption,
    mutually_exclusive=("socket",),
)
@option(
    "-p",
    "--port",
    default=5722,
    show_default=True,
    help="TCP port on which to listen",
    cls=MutuallyExclusiveOption,
    mutually_exclusive=("socket",),
)
@option(
    "-s",
    "--socket",
    help="Unix socket on which to bind to, Can't be used with --host and --port",
    cls=MutuallyExclusiveOption,
    mutually_exclusive=("host", "port"),
)
@option(
    "--processes",
    type=int,
    help="Number of processes to spawn. May not be supported by all servers",
)
@option(
    "--threads",
    type=int,
    help="Number of threads used to process application logic. May not be supported by all servers",
)
def main(server, host, port, socket, processes, threads):
    """Starts the Supysonic web server"""

    if server is None:
        server = find_first_available_server()
        if server is None:
            raise ClickException(
                "Couldn't load any server, please install one of {}".format(_servers)
            )
    else:
        try:
            server = get_server(server)
        except ImportError:
            raise ClickException(
                "Couldn't load {}, please install it first".format(server)
            )

    if socket is not None:
        host = None
        port = None

    app = create_application()
    server(
        app, host=host, port=port, socket=socket, processes=processes, threads=threads
    ).run()
