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"]


def _make_dev_command() -> click.Command:
    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.",
        ),
    ]

    @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

        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)

        uvicorn.run = patched_run
        try:
            ctx.invoke(_original_dev, **kwargs)
        finally:
            uvicorn.run = original_run

    return 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(_make_dev_command())
