from __future__ import annotations

import secrets
from typing import Any

import click

import edgy
from edgy.conf import settings


@click.option(
    "-p",
    "--port",
    type=int,
    default=8000,
    help="Port to run the development server.",
    show_default=True,
)
@click.option(
    "--host",
    type=str,
    default="localhost",
    help="Server host. Typically localhost.",
    show_default=True,
)
@click.option(
    "--admin-path",
    type=str,
    default=None,
    help='The sub-path under which the admin is available. Defaults to "/admin" if unset in settings',
    show_default=True,
)
@click.option(
    "--auth-name",
    type=str,
    default="admin",
    help="Basic auth name.",
    show_default=True,
)
@click.option(
    "--auth-pw",
    type=str,
    default=None,
    help="Basic auth password. By default autogenerated.",
    show_default=False,
)
@click.option(
    "--debug",
    help="Start the application in debug mode.",
    show_default=True,
    is_flag=True,
)
@click.option(
    "--create-all",
    help="Create all models when not existent.",
    show_default=True,
    is_flag=True,
)
@click.option(
    "--log-level",
    type=str,
    default="info",
    help="What log level should uvicorn run.",
    show_default=True,
)
@click.command(name="runserver")
def admin_serve(
    port: int,
    host: str,
    debug: bool,
    create_all: bool,
    log_level: str,
    auth_name: str,
    auth_pw: str | None,
    admin_path: str | None,
) -> None:
    """Starts the Edgy admin development server.

    The --app can be passed in the form of <module>.<submodule>:<app> or be set
    as environment variable LILYA_DEFAULT_APP.

    Alternatively, if none is passed, Lilya will perform the application discovery.

    It is strongly advised not to run this command in any pther environment but developmentyping.
    This was designed to facilitate the development environment and should not be used in pr

    How to run: `edgy admin_serve`
    """
    try:
        from lilya.apps import Lilya
        from lilya.middleware import DefineMiddleware
        from lilya.middleware.sessions import SessionMiddleware
        from lilya.permissions import DefinePermission
        from lilya.routing import Include
    except ImportError:
        raise RuntimeError("Lilya needs to be installed to run admin_serve.") from None
    try:
        import jinja2  # noqa
    except ImportError:
        raise RuntimeError("Jinja2 needs to be installed to run admin_serve.") from None
    try:
        import uvicorn
    except ImportError:
        raise RuntimeError("Uvicorn needs to be installed to run admin_serve.") from None
    from edgy.contrib.admin.permissions import BasicAuthAccess

    old_instance = edgy.monkay.instance
    if admin_path is not None:
        edgy.monkay.settings.admin_config.admin_prefix_url = admin_path
    if edgy.monkay.settings.admin_config.admin_prefix_url is None:
        edgy.monkay.settings.admin_config.admin_prefix_url = "/admin"

    if old_instance is None:
        raise RuntimeError(
            'You need to specify an app which registry is used. For experimenting use: "tests.cli.main"'
        )
    from edgy.contrib.admin.application import create_admin_app

    print_pw = False
    if auth_pw is None:
        auth_pw = secrets.token_urlsafe()
        print_pw = True
    admin_app = create_admin_app()
    routes = [
        Include(
            path=settings.admin_config.admin_prefix_url,
            app=admin_app,
            permissions=[
                DefinePermission(
                    BasicAuthAccess, print_pw=print_pw, username=auth_name, password=auth_pw
                )
            ],
            middleware=[
                DefineMiddleware(
                    SessionMiddleware,
                    secret_key=settings.admin_config.SECRET_KEY,
                    session_cookie="admin_session",
                ),
            ],
        ),
    ]
    if old_instance.app is not None:
        routes.append(Include(path="/", app=old_instance.app))
    app: Any = Lilya(
        routes=routes,
    )
    if debug:
        app.debug = True
        admin_app.debug = True
    app = old_instance.registry.asgi(app)
    if create_all:
        edgy.run_sync(old_instance.registry.create_all())
    edgy.monkay.set_instance(edgy.Instance(registry=old_instance.registry, app=app))

    uvicorn.run(
        app=app,
        port=port,
        host=host,
        reload=False,
        lifespan="on",
        log_level=log_level,
    )
