from __future__ import annotations
import importlib
import sys

import click
import typer
import uvicorn
from fastapi_cli.cli import app as _fastapi_typer_app

from asgi_autoreload import AutoreloadMiddleware

_fastapi_click = typer.main.get_command(_fastapi_typer_app)
_original_dev = _fastapi_click.commands["dev"]

params = list(_original_dev.params) + [
    click.Option(
        ["--watch"],
        multiple=True,
        default=["."],
        show_default=True,
        help="Paths to watch for browser reload.",
    ),
    click.Option(
        ["--exclude"],
        multiple=True,
        default=[],
        show_default=True,
        help="Paths to exclude from watching.",
    ),
]

# Remove the "reload" option, because we always want to enable autoreload
params = [param for param in params if param.name != "reload"]


def make_patched_run(
    build_command: list[str] | None,
    original_run: callable,
    watch: tuple[str, ...],
    exclude: tuple[str, ...],
) -> callable:
    def patched_run(app, **run_kwargs):  # type: ignore[no-untyped-def]
        if isinstance(app, str):
            if "" not in sys.path:
                sys.path.insert(0, "")
            mod_str, attr = app.rsplit(":", 1)
            app = getattr(importlib.import_module(mod_str), attr)
        wrapped = AutoreloadMiddleware(
            app,
            watch_paths=list(watch),
            build_command=build_command,
            exclude_paths=list(exclude) or None,
        )
        run_kwargs.pop("reload", None)  # incompatible with Python object
        original_run(wrapped, **run_kwargs)

    return patched_run


@click.pass_context
def callback(
    ctx: click.Context, watch: tuple[str, ...], exclude: tuple[str, ...], **kwargs
) -> None:
    build_command = ctx.args if ctx.args else None
    original_run = uvicorn.run

    uvicorn.run = make_patched_run(build_command, original_run, watch, exclude)
    try:
        ctx.invoke(_original_dev, **kwargs)
    finally:
        uvicorn.run = original_run


dev_command = click.Command(
    name="dev",
    callback=callback,
    params=params,
    help=_original_dev.help,
    context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
)


@click.group()
def main() -> None:
    pass


main.add_command(dev_command)
