"""Modern difference-in-differences estimators."""

import importlib as _importlib
from importlib.metadata import PackageNotFoundError, version
from typing import Any

# Version handling
try:
    __version__ = version("moderndid")
except PackageNotFoundError:
    __version__ = "unknown"

__all__ = [
    # GPU backend
    "HAS_CUPY",
    # did module
    "AGGTEResult",
    # Optional: didhonest (requires cvxpy)
    "APRCIResult",
    "ARPNuisanceCIResult",
    "ATTgtResult",
    # Optional: didcont (requires formulaic)
    "BSpline",
    "ComputeATTgtResult",
    # didtriple module
    "DDDATTgtRCResult",
    "DDDATTgtResult",
    "DDDAggResult",
    "DDDMultiPeriodRCResult",
    "DDDMultiPeriodResult",
    "DDDPanelResult",
    "DDDRCResult",
    # Core preprocessing
    "DIDData",
    # didinter module
    "DIDInterATEResult",
    "DIDInterEffectsResult",
    "DIDInterPlacebosResult",
    "DIDInterResult",
    "DeltaRMBResult",
    "DeltaRMMResult",
    "DeltaRMResult",
    "DeltaSDBResult",
    "DeltaSDMResult",
    "DeltaSDRMBResult",
    "DeltaSDRMMResult",
    "DeltaSDRMResult",
    "DeltaSDResult",
    "FLCIResult",
    "HonestDiDResult",
    "MPPretestResult",
    "MPResult",
    "MultivariateBasis",
    "NPIVResult",
    "OriginalCSResult",
    "PTEParams",
    # panel module
    "PanelDiagnostics",
    "SensitivityResult",
    "SplineBase",
    # Version
    "__version__",
    "_get_first_difference",
    "_get_group",
    "_make_balanced_panel",
    "affine_variance",
    "agg_ddd",
    "aggte",
    # drdid module
    "aipw_did_panel",
    "aipw_did_rc_imp1",
    "aipw_did_rc_imp2",
    # panel module (lowercase)
    "are_varying",
    "assign_rc_ids",
    "att_gt",
    "avoid_zero_division",
    "basis_dimension",
    "basis_vector",
    "bin_factor",
    "calculate_pscore_ipt",
    "complete_data",
    "compute_aggte",
    "compute_arp_ci",
    "compute_arp_nuisance_ci",
    "compute_att_gt",
    "compute_bounds",
    "compute_cck_ucb",
    "compute_conditional_cs_rm",
    "compute_conditional_cs_rmb",
    "compute_conditional_cs_rmm",
    "compute_conditional_cs_sd",
    "compute_conditional_cs_sdb",
    "compute_conditional_cs_sdm",
    "compute_conditional_cs_sdrm",
    "compute_conditional_cs_sdrmb",
    "compute_conditional_cs_sdrmm",
    "compute_delta_sd_lowerbound_m",
    "compute_delta_sd_upperbound_m",
    "compute_flci",
    "compute_identified_set_rm",
    "compute_identified_set_rmb",
    "compute_identified_set_rmm",
    "compute_identified_set_sd",
    "compute_identified_set_sdb",
    "compute_identified_set_sdm",
    "compute_identified_set_sdrm",
    "compute_identified_set_sdrmb",
    "compute_identified_set_sdrmm",
    "compute_least_favorable_cv",
    "compute_ucb",
    "compute_vlo_vup_dual",
    "construct_original_cs",
    "cont_did",
    "create_interactions",
    "create_monotonicity_constraint_matrix",
    "create_pre_period_constraint_matrix",
    "create_second_difference_matrix",
    "create_sensitivity_results_rm",
    "create_sensitivity_results_sm",
    "create_sign_constraint_matrix",
    # Core data
    "dask_att_gt",
    "dask_cont_did",
    "dask_ddd",
    "dask_did_multiplegt",
    "data",
    "ddd",
    "ddd_mp",
    "ddd_mp_rc",
    "ddd_panel",
    "ddd_rc",
    "deduplicate_panel",
    "diagnose_panel",
    "did_multiplegt",
    "drdid",
    "drdid_imp_local_rc",
    "drdid_imp_panel",
    "drdid_imp_rc",
    "drdid_panel",
    "drdid_rc",
    "drdid_trad_rc",
    "estimate_lowerbound_m_conditional_test",
    "extract_vars_from_formula",
    "fill_panel_gaps",
    "folded_normal_quantile",
    "format_aggte_result",
    "format_mp_pretest_result",
    "format_mp_result",
    "gen_dgp_2periods",
    "gen_dgp_mult_periods",
    "gen_dgp_scalable",
    "gen_did_scalable",
    "generate_simple_ddd_data",
    # GPU backend
    "get_backend",
    "get_first_difference",
    "get_group",
    "has_gaps",
    "honest_did",
    "ipw_did_panel",
    "ipw_did_rc",
    "ipw_rc",
    "ipwdid",
    "is_balanced_panel",
    "is_full_rank",
    "lee_coefficient",
    "load_cai2016",
    "load_ehec",
    "load_engel",
    "load_favara_imbs",
    "load_mpdta",
    "load_nsw",
    "lp_conditional_test",
    "make_balanced_panel",
    "matrix_sqrt",
    "maximize_bias",
    "mboot",
    "mboot_ddd",
    "mboot_did",
    "mboot_twfep_did",
    "minimize_variance",
    "mp",
    "mp_pretest",
    "npiv",
    "npiv_choose_j",
    "npiv_est",
    "npiv_j",
    "npiv_jhat_max",
    "ols_panel",
    "ordid",
    "panel_to_wide",
    "parse_formula",
    # Optional: plots (requires plotnine)
    "plot_agg",
    "plot_dose_response",
    "plot_event_study",
    "plot_gt",
    "plot_multiplegt",
    "plot_sensitivity",
    "preprocess_did",
    "print_did_result",
    "prodspline",
    "reg_did_panel",
    "reg_did_rc",
    "scan_gaps",
    "selection_matrix",
    "set_backend",
    "setup_pte",
    "setup_pte_basic",
    "setup_pte_cont",
    "simulate_cont_did_data",
    "spark_att_gt",
    "spark_cont_did",
    "spark_ddd",
    "spark_did_multiplegt",
    "std_ipw_did_panel",
    "std_ipw_did_rc",
    "summary_mp_pretest",
    "test_in_identified_set",
    "test_in_identified_set_flci_hybrid",
    "test_in_identified_set_lf_hybrid",
    "test_in_identified_set_max",
    "twfe_did_panel",
    "twfe_did_rc",
    "use_backend",
    "validate_conformable",
    "validate_symmetric_psd",
    "wboot_ddd",
    "wboot_dr_tr_panel",
    "wboot_drdid_imp_panel",
    "wboot_drdid_ipt_rc1",
    "wboot_drdid_ipt_rc2",
    "wboot_drdid_rc1",
    "wboot_drdid_rc2",
    "wboot_ipw_panel",
    "wboot_ipw_rc",
    "wboot_reg_panel",
    "wboot_reg_rc",
    "wboot_std_ipw_panel",
    "wboot_std_ipw_rc",
    "wboot_twfe_panel",
    "wboot_twfe_rc",
    "wide_to_panel",
    "wols_panel",
    "wols_rc",
]

# Submodules that can be loaded lazily
# Note: drdid is excluded because we want `from moderndid import drdid` to get
# the drdid() function, not the drdid submodule. Access submodule via moderndid.drdid.
_submodules = ["core", "did", "didinter", "didtriple", "didcont", "didhonest", "plots"]

# Map attribute names to their module paths (for base/always-available imports)
_lazy_imports = {
    # core.backend
    "HAS_CUPY": "moderndid.cupy.backend",
    "get_backend": "moderndid.cupy.backend",
    "set_backend": "moderndid.cupy.backend",
    "use_backend": "moderndid.cupy.backend",
    # core.data
    "data": "moderndid.core",
    "load_cai2016": "moderndid.core.data",
    "load_ehec": "moderndid.core.data",
    "load_engel": "moderndid.core.data",
    "load_favara_imbs": "moderndid.core.data",
    "load_mpdta": "moderndid.core.data",
    "load_nsw": "moderndid.core.data",
    "simulate_cont_did_data": "moderndid.core.data",
    # core.preprocess
    "DIDData": "moderndid.core.preprocess",
    "preprocess_did": "moderndid.core.preprocess",
    "extract_vars_from_formula": "moderndid.core.preprocess.utils",
    "parse_formula": "moderndid.core.preprocess.utils",
    # panel (core.panel)
    "PanelDiagnostics": "moderndid.core.panel",
    "are_varying": "moderndid.core.panel",
    "assign_rc_ids": "moderndid.core.panel",
    "complete_data": "moderndid.core.panel",
    "deduplicate_panel": "moderndid.core.panel",
    "diagnose_panel": "moderndid.core.panel",
    "fill_panel_gaps": "moderndid.core.panel",
    "get_first_difference": "moderndid.core.panel",
    "get_group": "moderndid.core.panel",
    "has_gaps": "moderndid.core.panel",
    "is_balanced_panel": "moderndid.core.panel",
    "make_balanced_panel": "moderndid.core.panel",
    "panel_to_wide": "moderndid.core.panel",
    "scan_gaps": "moderndid.core.panel",
    "wide_to_panel": "moderndid.core.panel",
    # did
    "AGGTEResult": "moderndid.did.aggte_obj",
    "format_aggte_result": "moderndid.did.format",
    "ATTgtResult": "moderndid.did.compute_att_gt",
    "ComputeATTgtResult": "moderndid.did.compute_att_gt",
    "compute_att_gt": "moderndid.did.compute_att_gt",
    "MPPretestResult": "moderndid.did.multiperiod_obj",
    "MPResult": "moderndid.did.multiperiod_obj",
    "format_mp_pretest_result": "moderndid.did.format",
    "format_mp_result": "moderndid.did.format",
    "mp": "moderndid.did.multiperiod_obj",
    "mp_pretest": "moderndid.did.multiperiod_obj",
    "summary_mp_pretest": "moderndid.did.multiperiod_obj",
    "aggte": "moderndid.did.aggte",
    "att_gt": "moderndid.did.att_gt",
    "compute_aggte": "moderndid.did.compute_aggte",
    "mboot": "moderndid.did.mboot",
    # drdid
    "drdid": "moderndid.drdid.drdid",
    "ipwdid": "moderndid.drdid.ipwdid",
    "ordid": "moderndid.drdid.ordid",
    "print_did_result": "moderndid.drdid.format",
    "drdid_imp_local_rc": "moderndid.drdid.estimators.drdid_imp_local_rc",
    "drdid_imp_panel": "moderndid.drdid.estimators.drdid_imp_panel",
    "drdid_imp_rc": "moderndid.drdid.estimators.drdid_imp_rc",
    "drdid_panel": "moderndid.drdid.estimators.drdid_panel",
    "drdid_rc": "moderndid.drdid.estimators.drdid_rc",
    "drdid_trad_rc": "moderndid.drdid.estimators.drdid_trad_rc",
    "ipw_did_panel": "moderndid.drdid.estimators.ipw_did_panel",
    "ipw_did_rc": "moderndid.drdid.estimators.ipw_did_rc",
    "reg_did_panel": "moderndid.drdid.estimators.reg_did_panel",
    "reg_did_rc": "moderndid.drdid.estimators.reg_did_rc",
    "std_ipw_did_panel": "moderndid.drdid.estimators.std_ipw_did_panel",
    "std_ipw_did_rc": "moderndid.drdid.estimators.std_ipw_did_rc",
    "twfe_did_panel": "moderndid.drdid.estimators.twfe_did_panel",
    "twfe_did_rc": "moderndid.drdid.estimators.twfe_did_rc",
    "ols_panel": "moderndid.drdid.estimators.wols",
    "wols_panel": "moderndid.drdid.estimators.wols",
    "wols_rc": "moderndid.drdid.estimators.wols",
    "wboot_ipw_rc": "moderndid.drdid.bootstrap.boot_ipw_rc",
    "mboot_did": "moderndid.drdid.bootstrap.boot_mult",
    "mboot_twfep_did": "moderndid.drdid.bootstrap.boot_mult",
    "wboot_dr_tr_panel": "moderndid.drdid.bootstrap.boot_panel",
    "wboot_drdid_imp_panel": "moderndid.drdid.bootstrap.boot_panel",
    "wboot_ipw_panel": "moderndid.drdid.bootstrap.boot_panel",
    "wboot_reg_panel": "moderndid.drdid.bootstrap.boot_panel",
    "wboot_std_ipw_panel": "moderndid.drdid.bootstrap.boot_panel",
    "wboot_twfe_panel": "moderndid.drdid.bootstrap.boot_panel",
    "wboot_drdid_rc1": "moderndid.drdid.bootstrap.boot_rc",
    "wboot_drdid_rc2": "moderndid.drdid.bootstrap.boot_rc",
    "wboot_drdid_ipt_rc1": "moderndid.drdid.bootstrap.boot_rc_ipt",
    "wboot_drdid_ipt_rc2": "moderndid.drdid.bootstrap.boot_rc_ipt",
    "wboot_reg_rc": "moderndid.drdid.bootstrap.boot_reg_rc",
    "wboot_std_ipw_rc": "moderndid.drdid.bootstrap.boot_std_ipw_rc",
    "wboot_twfe_rc": "moderndid.drdid.bootstrap.boot_twfe_rc",
    "aipw_did_panel": "moderndid.drdid.propensity.aipw_estimators",
    "aipw_did_rc_imp1": "moderndid.drdid.propensity.aipw_estimators",
    "aipw_did_rc_imp2": "moderndid.drdid.propensity.aipw_estimators",
    "ipw_rc": "moderndid.drdid.propensity.ipw_estimators",
    "calculate_pscore_ipt": "moderndid.drdid.propensity.pscore_ipt",
    # didinter
    "DIDInterATEResult": "moderndid.didinter.results",
    "DIDInterEffectsResult": "moderndid.didinter.results",
    "DIDInterPlacebosResult": "moderndid.didinter.results",
    "DIDInterResult": "moderndid.didinter.results",
    "did_multiplegt": "moderndid.didinter.did_multiplegt",
    # didtriple
    "DDDATTgtRCResult": "moderndid.didtriple.estimators.ddd_mp_rc",
    "DDDATTgtResult": "moderndid.didtriple.estimators.ddd_mp",
    "DDDAggResult": "moderndid.didtriple.agg_ddd_obj",
    "DDDMultiPeriodRCResult": "moderndid.didtriple.estimators.ddd_mp_rc",
    "DDDMultiPeriodResult": "moderndid.didtriple.estimators.ddd_mp",
    "DDDPanelResult": "moderndid.didtriple.estimators.ddd_panel",
    "DDDRCResult": "moderndid.didtriple.estimators.ddd_rc",
    "agg_ddd": "moderndid.didtriple.agg_ddd",
    "ddd": "moderndid.didtriple.ddd",
    "ddd_mp": "moderndid.didtriple.estimators.ddd_mp",
    "ddd_mp_rc": "moderndid.didtriple.estimators.ddd_mp_rc",
    "ddd_panel": "moderndid.didtriple.estimators.ddd_panel",
    "ddd_rc": "moderndid.didtriple.estimators.ddd_rc",
    "gen_dgp_2periods": "moderndid.didtriple.dgp",
    "gen_dgp_mult_periods": "moderndid.didtriple.dgp",
    "gen_dgp_scalable": "moderndid.didtriple.dgp",
    "gen_did_scalable": "moderndid.core.data",
    "generate_simple_ddd_data": "moderndid.didtriple.dgp",
    "mboot_ddd": "moderndid.didtriple.bootstrap",
    "wboot_ddd": "moderndid.didtriple.bootstrap",
}

# Optional imports that require extra dependencies
# Format: {name: (module_path, extra_name)}
_optional_imports = {
    # didcont (requires formulaic)
    "PTEParams": ("moderndid.didcont", "didcont"),
    "_get_first_difference": ("moderndid.didcont", "didcont"),
    "_get_group": ("moderndid.didcont", "didcont"),
    "_make_balanced_panel": ("moderndid.didcont", "didcont"),
    "avoid_zero_division": ("moderndid.didcont", "didcont"),
    "basis_dimension": ("moderndid.didcont", "didcont"),
    "cont_did": ("moderndid.didcont", "didcont"),
    "is_full_rank": ("moderndid.didcont", "didcont"),
    "matrix_sqrt": ("moderndid.didcont", "didcont"),
    "setup_pte": ("moderndid.didcont", "didcont"),
    "setup_pte_basic": ("moderndid.didcont", "didcont"),
    "setup_pte_cont": ("moderndid.didcont", "didcont"),
    "MultivariateBasis": ("moderndid.didcont.npiv", "didcont"),
    "NPIVResult": ("moderndid.didcont.npiv", "didcont"),
    "compute_cck_ucb": ("moderndid.didcont.npiv", "didcont"),
    "compute_ucb": ("moderndid.didcont.npiv", "didcont"),
    "npiv": ("moderndid.didcont.npiv", "didcont"),
    "npiv_choose_j": ("moderndid.didcont.npiv", "didcont"),
    "npiv_est": ("moderndid.didcont.npiv", "didcont"),
    "npiv_j": ("moderndid.didcont.npiv", "didcont"),
    "npiv_jhat_max": ("moderndid.didcont.npiv", "didcont"),
    "prodspline": ("moderndid.didcont.npiv", "didcont"),
    "BSpline": ("moderndid.didcont.spline", "didcont"),
    "SplineBase": ("moderndid.didcont.spline", "didcont"),
    # didhonest (requires cvxpy)
    "APRCIResult": ("moderndid.didhonest", "didhonest"),
    "ARPNuisanceCIResult": ("moderndid.didhonest", "didhonest"),
    "DeltaRMBResult": ("moderndid.didhonest", "didhonest"),
    "DeltaRMMResult": ("moderndid.didhonest", "didhonest"),
    "DeltaRMResult": ("moderndid.didhonest", "didhonest"),
    "DeltaSDBResult": ("moderndid.didhonest", "didhonest"),
    "DeltaSDMResult": ("moderndid.didhonest", "didhonest"),
    "DeltaSDResult": ("moderndid.didhonest", "didhonest"),
    "DeltaSDRMBResult": ("moderndid.didhonest", "didhonest"),
    "DeltaSDRMMResult": ("moderndid.didhonest", "didhonest"),
    "DeltaSDRMResult": ("moderndid.didhonest", "didhonest"),
    "FLCIResult": ("moderndid.didhonest", "didhonest"),
    "HonestDiDResult": ("moderndid.didhonest", "didhonest"),
    "OriginalCSResult": ("moderndid.didhonest", "didhonest"),
    "SensitivityResult": ("moderndid.didhonest", "didhonest"),
    "affine_variance": ("moderndid.didhonest", "didhonest"),
    "basis_vector": ("moderndid.didhonest", "didhonest"),
    "bin_factor": ("moderndid.didhonest", "didhonest"),
    "compute_arp_ci": ("moderndid.didhonest", "didhonest"),
    "compute_arp_nuisance_ci": ("moderndid.didhonest", "didhonest"),
    "compute_bounds": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_rm": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_rmb": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_rmm": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_sd": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_sdb": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_sdm": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_sdrm": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_sdrmb": ("moderndid.didhonest", "didhonest"),
    "compute_conditional_cs_sdrmm": ("moderndid.didhonest", "didhonest"),
    "compute_delta_sd_lowerbound_m": ("moderndid.didhonest", "didhonest"),
    "compute_delta_sd_upperbound_m": ("moderndid.didhonest", "didhonest"),
    "compute_flci": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_rm": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_rmb": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_rmm": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_sd": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_sdb": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_sdm": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_sdrm": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_sdrmb": ("moderndid.didhonest", "didhonest"),
    "compute_identified_set_sdrmm": ("moderndid.didhonest", "didhonest"),
    "compute_least_favorable_cv": ("moderndid.didhonest", "didhonest"),
    "compute_vlo_vup_dual": ("moderndid.didhonest", "didhonest"),
    "construct_original_cs": ("moderndid.didhonest", "didhonest"),
    "create_interactions": ("moderndid.didhonest", "didhonest"),
    "create_monotonicity_constraint_matrix": ("moderndid.didhonest", "didhonest"),
    "create_pre_period_constraint_matrix": ("moderndid.didhonest", "didhonest"),
    "create_second_difference_matrix": ("moderndid.didhonest", "didhonest"),
    "create_sensitivity_results_rm": ("moderndid.didhonest", "didhonest"),
    "create_sensitivity_results_sm": ("moderndid.didhonest", "didhonest"),
    "create_sign_constraint_matrix": ("moderndid.didhonest", "didhonest"),
    "estimate_lowerbound_m_conditional_test": ("moderndid.didhonest", "didhonest"),
    "folded_normal_quantile": ("moderndid.didhonest", "didhonest"),
    "honest_did": ("moderndid.didhonest", "didhonest"),
    "lee_coefficient": ("moderndid.didhonest", "didhonest"),
    "lp_conditional_test": ("moderndid.didhonest", "didhonest"),
    "maximize_bias": ("moderndid.didhonest", "didhonest"),
    "minimize_variance": ("moderndid.didhonest", "didhonest"),
    "selection_matrix": ("moderndid.didhonest", "didhonest"),
    "test_in_identified_set": ("moderndid.didhonest", "didhonest"),
    "test_in_identified_set_flci_hybrid": ("moderndid.didhonest", "didhonest"),
    "test_in_identified_set_lf_hybrid": ("moderndid.didhonest", "didhonest"),
    "test_in_identified_set_max": ("moderndid.didhonest", "didhonest"),
    "validate_conformable": ("moderndid.didhonest", "didhonest"),
    "validate_symmetric_psd": ("moderndid.didhonest", "didhonest"),
    # dask (requires dask + distributed)
    "dask_att_gt": ("moderndid.dask", "dask"),
    "dask_cont_did": ("moderndid.dask", "dask"),
    "dask_ddd": ("moderndid.dask", "dask"),
    "dask_did_multiplegt": ("moderndid.dask", "dask"),
    # spark (requires pyspark)
    "spark_att_gt": ("moderndid.spark", "spark"),
    "spark_cont_did": ("moderndid.spark", "spark"),
    "spark_ddd": ("moderndid.spark", "spark"),
    "spark_did_multiplegt": ("moderndid.spark", "spark"),
    # plots (requires plotnine)
    "plot_agg": ("moderndid.plots", "plots"),
    "plot_dose_response": ("moderndid.plots", "plots"),
    "plot_event_study": ("moderndid.plots", "plots"),
    "plot_gt": ("moderndid.plots", "plots"),
    "plot_multiplegt": ("moderndid.plots", "plots"),
    "plot_sensitivity": ("moderndid.plots", "plots"),
}

# Aliased imports (different name in source module)
_aliases = {
    "DIDInterATEResult": ("moderndid.didinter.results", "ATEResult"),
    "DIDInterEffectsResult": ("moderndid.didinter.results", "EffectsResult"),
    "DIDInterPlacebosResult": ("moderndid.didinter.results", "PlacebosResult"),
    "DDDATTgtRCResult": ("moderndid.didtriple.estimators.ddd_mp_rc", "ATTgtRCResult"),
    "DDDATTgtResult": ("moderndid.didtriple.estimators.ddd_mp", "ATTgtResult"),
}


def __getattr__(name: str) -> Any:
    # Aliased imports
    if name in _aliases:
        module_path, attr_name = _aliases[name]
        module = _importlib.import_module(module_path)
        return getattr(module, attr_name)

    # Base lazy imports (always available) - check before submodules
    # so that e.g. `drdid` resolves to the function, not the module
    if name in _lazy_imports:
        module = _importlib.import_module(_lazy_imports[name])
        return getattr(module, name)

    # Optional imports (require extra dependencies)
    if name in _optional_imports:
        module_path, extra = _optional_imports[name]
        try:
            module = _importlib.import_module(module_path)
            return getattr(module, name)
        except ImportError as e:
            raise ImportError(f"'{name}' requires extra dependencies: uv pip install 'moderndid[{extra}]'") from e

    # Submodules - checked last so specific imports take precedence
    if name in _submodules:
        return _importlib.import_module(f"moderndid.{name}")

    raise AttributeError(f"module 'moderndid' has no attribute {name!r}")


# Eagerly import names that shadow subpackages
# This is needed because `from X import Y` bypasses __getattr__ when Y is a subpackage
from moderndid.drdid.drdid import drdid


def __dir__() -> list[str]:
    return __all__
