import pandas as pd


def modelsummary(models, *, coef_omit=None, stars=True, gof_map=("nobs",), output="dataframe"):
    if not isinstance(models, (list, tuple)):
        models = [models]

    def star(p):
        if p < 0.01:
            return "***"
        if p < 0.05:
            return "**"
        if p < 0.10:
            return "*"
        return ""

    cols = []
    for j, model in enumerate(models, start=1):
        params = model.params
        pvals = model.pvalues
        if stars:
            s = params.map(lambda v: f"{v:.3f}") + pvals.map(star)
        else:
            s = params.map(lambda v: f"{v:.3f}")
        s.name = f"Model {j}"
        cols.append(s)

    tbl = pd.concat(cols, axis=1).reset_index(names="term")

    if coef_omit is not None:
        tbl = tbl.loc[~tbl["term"].str.contains(coef_omit, regex=True)]

    gof_rows = []
    if gof_map and ("nobs" in gof_map or "nobs" in set(gof_map)):
        row = {"term": "nobs"}
        for j, model in enumerate(models, start=1):
            row[f"Model {j}"] = f"{int(model.nobs)}"
        gof_rows.append(row)

    if gof_rows:
        tbl = pd.concat([tbl, pd.DataFrame(gof_rows)], ignore_index=True)

    if output == "dataframe":
        return tbl
    if output == "styler":
        return tbl.style.hide(axis="index")
    if output == "gt":
        from great_tables import GT

        return GT(tbl).sub_missing(missing_text="")

    raise ValueError(f"Unknown output={output!r}")
