#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
fetchez.registry
~~~~~~~~~~~~~~~~

This module contains the Module Registry for the Fetchez library.

:copyright: (c) 2010-2026 Regents of the University of Colorado
:license: MIT, see LICENSE for more details.
"""

import logging
import importlib.metadata
import copy
from typing import Dict, Any, cast

logger = logging.getLogger(__name__)


# =============================================================================
# Fetchez Registry
#
# Each module in the registry should have *AT LEAST* `mod` and `cls` defined
# correctly for the specific module so fetchez can call it correctly.
#
# It is highly encouraged to fill out all the metadata for every fetchez module.
# In addition to the *MANDATORY* fields mentioned above, we would also like:
#   `category`, `desc`, `agency`, `tags, `region`, `resolution`, `license` and `urls`
#
# This will ensure the registry is robust and useful for everyone.
#
# You can also set the optional `aliases` field, to give the module one or more alias.
# If a fetchez module has the same metadata as one that already exists, you
# can set the `inherits` key to that other module to avoid duplicating
# metadata. (`mod` and `cls` still need to set correctly.
# =============================================================================
class FetchezRegistry:
    """Fetchez Module Registry with expanded metadata for discovery."""

    _modules = {
        # Generic https module to send an argument to FetchModule.results
        "https": {"mod": "fetchez.core", "cls": "HttpDataset", "category": "Generic"},
        "file": {
            "mod": "fetchez.modules.path",
            "cls": "LocalDataset",
            "category": "Generic",
            "desc": "Process a local file path through the pipeline.",
            "agency": "Local",
            "tags": ["local", "file", "system", "path"],
            "region": "Global",
        },
        # Local module - query a local fred geojson
        "local": {
            "mod": "fetchez.modules.local",
            "cls": "Local",
            "category": "User Data",
            "desc": "Query local or custom FRED indices",
            "tags": ["local", "custom", "index"],
            "region": "Custom",
            "resolution": "N/A",
        },
        # GMRT
        "gmrt": {
            "mod": "fetchez.modules.gmrt",
            "cls": "GMRT",
            "category": "Bathymetry",
            "desc": "Global Multi-Resolution Topography Synthesis",
            "agency": "Lamont-Doherty Earth Observatory",
            "tags": ["bathymetry", "ocean", "global", "synthesis", "grid"],
            "region": "Global",
            "resolution": "Varies (100m - 400m)",
            "license": "CC-BY-4.0",
            "urls": {
                "home": "https://www.gmrt.org",
                "citation": "https://www.gmrt.org/about/citation.php",
            },
        },
        "gebco": {
            "mod": "fetchez.modules.gebco",
            "cls": "GEBCO",
            "category": "Bathymetry",
            "desc": "General Bathymetric Chart of the Oceans (GEBCO)",
            "agency": "GEBCO / IHO / IOC",
            "tags": ["gebco", "bathymetry", "global", "wcs", "tid"],
            "region": "Global",
            "resolution": "15 arc-seconds (~500m)",
            "license": "Public Domain / Attribution",
            "urls": {"home": "https://www.gebco.net/"},
        },
        # Copernicus DEMs
        "copernicus": {
            "mod": "fetchez.modules.copernicus",
            "cls": "CopernicusDEM",
            "category": "Topography",
            "desc": "Copernicus Global/European Digital Elevation Models (COP-30/10)",
            "agency": "ESA/Eurostat",
            "tags": ["satellite", "dsm", "radar", "global", "europe"],
            "region": "Global (COP-30) / Europe (COP-10)",
            "resolution": "30m / 10m",
            "license": "Open Data / attribution required",
            "urls": {
                "home": "https://ec.europa.eu/eurostat/web/gisco/geodata/reference-data/elevation/copernicus-dem",
                "docs": "https://spacedata.copernicus.eu/collections/copernicus-digital-elevation-model",
            },
        },
        "fabdem": {
            "mod": "fetchez.modules.fabdem",
            "cls": "FABDEM",
            "category": "Topography",
            "desc": "FABDEM (Forest And Buildings removed Copernicus DEM)",
            "agency": "University of Bristol",
            "tags": ["fabdem", "dem", "dtm", "copernicus", "global", "30m"],
            "region": "Global",
            "resolution": "1 arc-second (~30m)",
            "license": "CC-BY-NC-SA 4.0",
            "urls": {
                "home": "https://data.bris.ac.uk/data/dataset/s5hqmjcdj8yo2ibzi9b4ew3sn"
            },
        },
        # Copernicus CDSE Direct Node Fetcher
        "cdse": {
            "mod": "fetchez.modules.cdse",
            "cls": "CDSE",
            "category": "Imagery",
            "desc": "Copernicus Data Space Ecosystem (CDSE) Direct Node Fetcher",
            "agency": "ESA",
            "tags": ["cdse", "copernicus", "esa", "satellite", "odata", "imagery"],
            "region": "Global",
            "resolution": "Varies",
            "license": "Copernicus Open Access",
            "urls": {
                "home": "https://dataspace.copernicus.eu/",
                "auth": "https://identity.dataspace.copernicus.eu/auth/realms/CDSE/account/",
            },
        },
        "sentinel2_jp2": {
            "inherits": "cdse",
            "cls": "Sentinel2_CDSE",
            "desc": "Sentinel-2 RGB Bands (JP2) via CDSE Nodes",
            "tags": ["sentinel", "sentinel2", "imagery", "jp2", "optical", "rgb"],
            "resolution": "10m",
        },
        "sentinel2": {
            "mod": "fetchez.modules.sentinel2",
            "cls": "Sentinel2",
            "category": "Imagery",
            "desc": "Copernicus Sentinel-2 (Optical Imagery)",
            "agency": "ESA",
            "tags": [
                "sentinel",
                "satellite",
                "imagery",
                "copernicus",
                "esa",
                "optical",
            ],
            "region": "Global",
            "resolution": "10m / 20m / 60m",
            "license": "Copernicus Open Access",
            "urls": {
                "home": "https://dataspace.copernicus.eu/",
                "auth": "https://identity.dataspace.copernicus.eu/auth/realms/CDSE/account/",
            },
        },
        # Digital Coast (DAV) and shortcuts (slr, cudem, coned)
        "dav": {
            "mod": "fetchez.modules.dav",
            "cls": "DAV",
            "aliases": ["digital_coast"],
            "category": "Multidisciplinary",
            "desc": "NOAA Digital Coast Data Access Viewer",
            "agency": "NOAA",
            "tags": ["noaa", "lidar", "imagery", "dem", "digital coast", "landcover"],
            "region": "USA",
            "resolution": "Variable",
            "license": "Public Domain",
            "urls": {"home": "https://coast.noaa.gov/dataviewer/"},
        },
        "slr": {
            "mod": "fetchez.modules.dav",
            "cls": "SLR",
            "category": "Topography",
            "desc": "NOAA Sea Level Rise DEMs",
            "agency": "NOAA",
            "tags": ["slr", "dem", "elevation", "coastal"],
            "region": "USA / Coastal",
            "resolution": "Various",
            "license": "Public Domain",
            "urls": {"home": "https://coast.noaa.gov/slr/"},
        },
        "coned": {
            "mod": "fetchez.modules.dav",
            "cls": "CoNED",
            "category": "Topography",
            "desc": "Coastal National Elevation Database (CoNED)",
            "agency": "USGS / NOAA",
            "tags": ["coned", "topobathy", "dem"],
            "region": "USA / Coastal",
            "resolution": "1m - 3m",
            "license": "Public Domain",
            "urls": {"home": "https://www.usgs.gov/core-science-systems/ngp/coned"},
        },
        "cudem": {
            "mod": "fetchez.modules.dav",
            "cls": "CUDEM",
            "category": "Bathymetry",
            "desc": "Continuously Updated DEM (CUDEM)",
            "agency": "NOAA",
            "tags": ["cudem", "bathymetry", "dem"],
            "region": "USA / Coastal",
            "resolution": "1/9 Arc-Second",
            "license": "Public Domain",
            "urls": {"home": "https://coast.noaa.gov/digitalcoast/data/cudem.html"},
        },
        # NOAA ETOPO-2022
        "etopo": {
            "mod": "fetchez.modules.etopo",
            "cls": "ETOPO",
            "category": "Topography",
            "desc": "ETOPO 2022 Global Relief Model (15s, 30s, 60s)",
            "agency": "NOAA NCEI",
            "tags": [
                "topography",
                "bathymetry",
                "elevation",
                "global",
                "etopo",
                "relief",
            ],
            "region": "Global",
            "resolution": "15s (~450m) to 60s (~1.8km)",
            "license": "Public Domain",
            "urls": {
                "home": "https://www.ncei.noaa.gov/products/etopo-global-relief-model"
            },
        },
        # The National Map (TNM) / USGS and Shortcuts (ned/3dep/etc)
        "tnm": {
            "mod": "fetchez.modules.tnm",
            "cls": "TheNationalMap",
            "category": "Topography",
            "desc": "USGS 3DEP Products (NED, Lidar, Hydro) via The National Map",
            "agency": "USGS",
            "tags": ["usgs", "ned", "3dep", "lidar", "usa", "elevation"],
            "region": "USA",
            "resolution": "Varies (1m - 1 arc-second)",
            "license": "Public Domain (USGS)",
            "urls": {
                "home": "https://apps.nationalmap.gov/",
                "api": "https://tnmaccess.nationalmap.gov/api/v1/docs/",
            },
        },
        "ned": {
            "inherits": "tnm",
            "mod": "fetchez.modules.tnm",
            "cls": "NED",
            "category": "Topography",
            "desc": 'USGS Seamless DEMs (1m, 1/3", 1")',
            "aliases": ["3dep_dem", "NED"],
        },
        "3dep": {
            "inherits": "tnm",
            "mod": "fetchez.modules.tnm",
            "cls": "TNM_LAZ",
            "category": "Topography",
            "desc": "USGS 3DEP Lidar Point Clouds (LAZ)",
            "aliases": ["3dep_lidar"],
        },
        # USGS Checkpoints
        "3dep_cp": {
            "mod": "fetchez.modules.checkpoints_3dep",
            "cls": "CheckPoints3DEP",
            "category": "Reference",
            "desc": "USGS 3DEP Elevation Validation Checkpoints",
            "agency": "USGS",
            "tags": [
                "usgs",
                "3dep",
                "checkpoints",
                "validation",
                "accuracy",
                "control-points",
            ],
            "region": "USA",
            "resolution": "Point Data",
            "license": "Public Domain",
            "urls": {
                "home": "https://www.usgs.gov/3d-elevation-program",
                "source": "https://www.sciencebase.gov/catalog/item/67075e6bd34e969edc59c3e7",
            },
        },
        # USGS Waterservices
        "waterservices": {
            "mod": "fetchez.modules.waterservices",
            "cls": "WaterServices",
            "category": "Hydrography",
            "desc": "USGS Water Services (Streamflow/River Data)",
            "agency": "USGS",
            "tags": ["water", "streamflow", "river", "usgs", "hydrology", "realtime"],
            "region": "USA",
            "resolution": "Point Data",
            "license": "Public Domain",
            "urls": {"home": "https://waterservices.usgs.gov/"},
        },
        # NASA EarthData via EarthAccess
        "earthaccess": {
            "mod": "fetchez.modules.earthaccess",
            "cls": "EarthAccess",
            "category": "Generic",
            "desc": "NASA Earth Science Data via EarthAccess",
            "agency": "NASA",
            "tags": [
                "nasa",
                "cmr",
                "harmony",
                "satellite",
                "earth-science",
                "remote-sensing",
                "nsidc",
            ],
            "region": "Global",
            "resolution": "Varies",
            "license": "NASA Data and Information Policy (Open)",
            "urls": {
                "home": "https://earthdata.nasa.gov/",
                "search": "https://search.earthdata.nasa.gov/",
                "git": "https://github.com/nsidc/earthaccess",
            },
        },
        # NASA EarthData
        "earthdata": {
            "mod": "fetchez.modules.earthdata",
            "cls": "EarthData",
            "category": "Generic",
            "desc": "NASA Earth Science Data via CMR & Harmony",
            "agency": "NASA",
            "tags": [
                "nasa",
                "cmr",
                "harmony",
                "satellite",
                "earth-science",
                "remote-sensing",
            ],
            "region": "Global",
            "resolution": "Varies",
            "license": "NASA Data and Information Policy (Open)",
            "urls": {
                "home": "https://earthdata.nasa.gov/",
                "search": "https://search.earthdata.nasa.gov/",
            },
        },
        # EarthData Shortcuts
        "icesat2": {
            "inherits": "earthdata",
            "cls": "IceSat2",
            "category": "Topography",
            "desc": "NASA IceSat-2 Laser Altimetry (ATL03/ATL08)",
            "tags": ["lidar", "icesat2", "atl03", "atl08", "elevation", "photon"],
            "resolution": "Photon / 100m",
        },
        "swot": {
            "inherits": "earthdata",
            "cls": "SWOT",
            "category": "Oceanography",
            "desc": "Surface Water and Ocean Topography (SWOT) Ka-band Radar",
            "tags": ["swot", "hydrology", "oceanography", "water-height", "karin"],
        },
        "mur_sst": {
            "inherits": "earthdata",
            "cls": "MUR_SST",
            "category": "Oceanography",
            "desc": "MUR Level 4 Global Sea Surface Temperature",
            "tags": ["sst", "temperature", "ocean", "mur", "l4"],
            "resolution": "0.01 degree (~1km)",
        },
        # Multibeam (NOAA)
        "multibeam": {
            "mod": "fetchez.modules.multibeam",
            "cls": "Multibeam",
            "category": "Bathymetry",
            "desc": "NOAA NCEI Multibeam Bathymetry (Global)",
            "agency": "NOAA NCEI",
            "tags": ["bathymetry", "multibeam", "ocean", "sonar", "noaa", "ncei"],
            "region": "Global",
            "resolution": "Varies (10m - 100m)",
            "license": "Public Domain",
            "urls": {"home": "https://www.ngdc.noaa.gov/mgg/bathymetry/multibeam.html"},
        },
        "mbdb": {
            "mod": "fetchez.modules.multibeam",
            "cls": "MBDB",
            "category": "Bathymetry",
            "desc": "NOAA Multibeam via ArcGIS Feature Server",
            "agency": "NOAA NCEI",
            "tags": ["bathymetry", "arcgis", "feature-server"],
            "inherits": "multibeam",
        },
        "r2r": {
            "mod": "fetchez.modules.multibeam",
            "cls": "R2R",
            "category": "Bathymetry",
            "desc": "Rolling Deck to Repository (R2R) Multibeam",
            "agency": "NSF / R2R",
            "tags": ["bathymetry", "research-vessels", "academic", "r2r"],
            "region": "Global",
            "resolution": "Raw Swath Data",
            "license": "Academic / Public",
            "urls": {"home": "https://www.rvdata.us/"},
        },
        # HydroNOS
        "nos_hydro": {
            "mod": "fetchez.modules.hydronos",
            "cls": "HydroNOS",
            "category": "Bathymetry",
            "desc": "NOAA NOS Hydrographic Surveys (BAG & XYZ)",
            "agency": "NOAA NOS",
            "tags": ["bathymetry", "hydrography", "nos", "noaa", "bag", "soundings"],
            "region": "USA / Coastal",
            "resolution": "Varies (0.5m - 30m)",
            "license": "Public Domain",
            "urls": {"home": "https://www.ngdc.noaa.gov/mgg/bathymetry/hydro.html"},
        },
        # Shortcut for just BAGs
        "bag": {
            "mod": "fetchez.modules.hydronos",
            "cls": "HydroNOS",
            "category": "Bathymetry",
            "desc": "NOAA NOS Bathymetric Attributed Grids (BAG)",
            "inherits": "nos_hydro",
        },
        # Coast Survey's BlueTopo
        "bluetopo": {
            "mod": "fetchez.modules.bluetopo",
            "cls": "BlueTopo",
            "category": "Bathymetry",
            "desc": "NOAA BlueTopo (National Bathymetric Source)",
            "agency": "NOAA OCS",
            "tags": ["bathymetry", "noaa", "bluetopo", "nbs", "ocean", "elevation"],
            "region": "USA",
            "resolution": "Variable",
            "license": "Public Domain",
            "urls": {"home": "https://nauticalcharts.noaa.gov/data/bluetopo.html"},
        },
        "ehydro": {
            "mod": "fetchez.modules.ehydro",
            "cls": "eHydro",
            "category": "Bathymetry",
            "desc": "USACE eHydro Navigation Surveys",
            "agency": "USACE",
            "tags": ["ehydro", "usace", "bathymetry", "navigation", "channels", "army"],
            "region": "USA / Coastal",
            "resolution": "Various (High-Res)",
            "license": "Public Domain",
            "urls": {"home": "https://navigation.usace.army.mil/Survey/Hydro"},
        },
        "mgds": {
            "mod": "fetchez.modules.mgds",
            "cls": "MGDS",
            "category": "Bathymetry",
            "desc": "Marine Geoscience Data System (MGDS)",
            "agency": "IEDA / NSF",
            "tags": ["mgds", "bathymetry", "marine", "swath", "geophysics"],
            "region": "Global / Marine",
            "resolution": "Various",
            "license": "Public / Academic",
            "urls": {"home": "https://www.marine-geo.org/"},
        },
        "margrav": {
            "mod": "fetchez.modules.margrav",
            "cls": "MarGrav",
            "category": "Bathymetry",
            "desc": "Marine Gravity (Scripps/UCSD)",
            "agency": "SIO",
            "tags": ["gravity", "bathymetry", "scripps", "altimetry"],
            "region": "Global / Marine",
            "resolution": "1-minute",
            "license": "Public Domain",
            "urls": {"home": "https://topex.ucsd.edu/WWW_html/mar_grav.html"},
        },
        "srtm_plus": {
            "mod": "fetchez.modules.srtmplus",
            "cls": "SRTMPlus",
            "category": "Topography",
            "desc": "SRTM15+ Global Bathymetry/Topography (Scripps/UCSD)",
            "agency": "SIO / UCSD",
            "tags": ["srtm", "bathymetry", "topography", "global", "scripps"],
            "region": "Global",
            "resolution": "15 arc-seconds (~500m)",
            "license": "Public Domain",
            "urls": {"home": "https://topex.ucsd.edu/WWW_html/srtm15_plus.html"},
        },
        "synbath": {
            "mod": "fetchez.modules.synbath",
            "cls": "SynBath",
            "category": "Bathymetry",
            "desc": "UCSD SynBath Global Synthetic Bathymetry",
            "agency": "SIO / UCSD",
            "tags": ["synbath", "bathymetry", "global", "scripps", "synthetic"],
            "region": "Global",
            "resolution": "30 arc-seconds (~1km)",
            "license": "Public Domain",
            "urls": {"home": "https://topex.ucsd.edu/pub/synbath/"},
        },
        "vgg": {
            "mod": "fetchez.modules.vgg",
            "cls": "VGG",
            "category": "Bathymetry",
            "desc": "Vertical Gravity Gradient (VGG)",
            "agency": "SIO / UCSD",
            "tags": ["vgg", "gravity", "curvature", "seamounts", "scripps"],
            "region": "Global",
            "resolution": "1-minute",
            "license": "Public Domain",
            "urls": {"home": "https://topex.ucsd.edu/WWW_html/mar_grav.html"},
        },
        # Nautical Charts (NOAA)
        "charts": {
            "mod": "fetchez.modules.charts",
            "cls": "NOAACharts",
            "category": "Reference",
            "desc": "NOAA Nautical Charts (ENC)",
            "agency": "NOAA NOS",
            "tags": ["charts", "nautical", "enc", "rnc", "navigation", "ocean"],
            "region": "USA / Coastal",
            "resolution": "Varies",
            "license": "Public Domain",
            "urls": {"home": "https://www.charts.noaa.gov/"},
        },
        # World Settlement Footprint
        "wsf": {
            "mod": "fetchez.modules.wsf",
            "cls": "WSF",
            "category": "Land Cover",
            "desc": "World Settlement Footprint 2019 (10m)",
            "agency": "DLR",
            "tags": ["urban", "settlement", "population", "dlr", "land-cover"],
            "region": "Global",
            "resolution": "10m",
            "license": "CC-BY 4.0",
            "urls": {"home": "https://geoservice.dlr.de/web/maps/wsf2019"},
        },
        ## Crowd-Sourced Bathymetry
        "csb": {
            "mod": "fetchez.modules.csb",
            "cls": "CSB",
            "category": "Bathymetry",
            "desc": "NOAA Crowd Sourced Bathymetry (CSB)",
            "agency": "NOAA NCEI",
            "tags": [
                "bathymetry",
                "crowd-sourced",
                "citizen-science",
                "csv",
                "depth",
                "noaa",
            ],
            "region": "Global",
            "resolution": "Varies",
            "license": "CC0 / Public Domain",
            "urls": {"home": "https://www.ngdc.noaa.gov/iho/"},
        },
        # NOAA Buoys
        "buoys": {
            "mod": "fetchez.modules.buoys",
            "cls": "Buoys",
            "category": "Oceanography",
            "desc": "NOAA NDBC Buoy Data (Realtime & Historical)",
            "agency": "NOAA NDBC",
            "tags": ["buoy", "ocean", "waves", "wind", "meteorology", "noaa"],
            "region": "Global (NOAA network)",
            "resolution": "Point Data",
            "license": "Public Domain",
            "urls": {"home": "https://www.ndbc.noaa.gov/"},
        },
        "tides": {
            "mod": "fetchez.modules.tides",
            "cls": "Tides",
            "category": "Oceanography",
            "desc": "NOAA Tides & Currents (CO-OPS)",
            "agency": "NOAA",
            "tags": ["tides", "water level", "co-ops", "oceanography", "stations"],
            "region": "USA / Coastal",
            "resolution": "Temporal (6-min / Hourly)",
            "license": "Public Domain",
            "urls": {"home": "https://tidesandcurrents.noaa.gov/"},
        },
        "bing": {
            "mod": "fetchez.modules.bing",
            "cls": "Bing",
            "category": "Reference",
            "desc": "Microsoft Global ML Building Footprints",
            "agency": "Microsoft",
            "tags": ["buildings", "footprints", "ai", "ml", "vector"],
            "region": "Global",
            "resolution": "Vector",
            "license": "ODbL",
            "urls": {"home": "https://github.com/microsoft/GlobalMLBuildingFootprints"},
        },
        "gba": {
            "mod": "fetchez.modules.gba",
            "cls": "GBA",
            "category": "Reference",
            "desc": "Global Building Atlas (LOD1 Footprints)",
            "agency": "TUM / DLR",
            "tags": ["buildings", "gba", "footprints", "lod1", "urban", "3d"],
            "region": "Global (Partial Coverage)",
            "resolution": "Vector",
            "license": "Academic / Research",
            "urls": {"home": "https://www.wk.bgu.tum.de/en/global-building-atlas/"},
        },
        # CHS NONNA from Canada
        "chs": {
            "mod": "fetchez.modules.chs",
            "cls": "CHS",
            "category": "Bathymetry",
            "desc": "Canadian Hydrographic Service NONNA (10m & 100m)",
            "agency": "CHS",
            "tags": ["bathymetry", "canada", "chs", "nonna", "wcs", "topography"],
            "region": "Canada",
            "resolution": "10m or 100m",
            "license": "Open Government Licence - Canada",
            "urls": {
                "home": "https://open.canada.ca/data/en/dataset/d3881c4c-650d-4070-bf9b-1e00aabf0a1d"
            },
        },
        "hrdem": {
            "mod": "fetchez.modules.hrdem",
            "cls": "HRDEM",
            "category": "Topography",
            "desc": "Canada HRDEM (Mosaic STAC & Legacy)",
            "agency": "NRCAN",
            "tags": ["canada", "hrdem", "nrcan", "lidar", "mosaic", "stac"],
            "region": "Canada",
            "resolution": "1m / 2m",
            "license": "Open Government Licence - Canada",
            "urls": {
                "home": "https://open.canada.ca/data/en/dataset/957782bf-847c-4644-a757-e383c0057995"
            },
        },
        "wadnr": {
            "mod": "fetchez.modules.wadnr",
            "cls": "WADNR",
            "category": "Topography",
            "desc": "Washington State DNR LiDAR",
            "agency": "WA DNR",
            "tags": ["lidar", "washington", "dnr", "wa", "elevation", "point-cloud"],
            "region": "Washington State",
            "resolution": "High-Res LiDAR",
            "license": "Public Domain",
            "urls": {"home": "https://lidarportal.dnr.wa.gov/"},
        },
        "nswtb": {
            "mod": "fetchez.modules.nswtb",
            "cls": "NSWTB",
            "category": "Bathymetry",
            "desc": "NSW Marine LiDAR Topo-Bathy (Contours/DEM)",
            "agency": "NSW Government",
            "tags": [
                "nsw",
                "australia",
                "bathymetry",
                "lidar",
                "contours",
                "topo-bathy",
            ],
            "region": "Australia (NSW Coast)",
            "resolution": "5m Contours / 5m DEM",
            "license": "Creative Commons Attribution",
            "urls": {
                "home": "https://datasets.seed.nsw.gov.au/dataset/marine-lidar-topo-bathy-2018"
            },
        },
        "emodnet": {
            "mod": "fetchez.modules.emodnet",
            "cls": "EMODNet",
            "category": "Bathymetry",
            "desc": "EMODnet Bathymetry (Europe)",
            "agency": "EU / EMODnet",
            "tags": ["bathymetry", "europe", "emodnet", "wcs", "erddap"],
            "region": "Europe",
            "resolution": "~115m (1/16 arc-min)",
            "license": "Open Data",
            "urls": {"home": "https://portal.emodnet-bathymetry.eu/"},
        },
        "gedtm30": {
            "mod": "fetchez.modules.gedtm30",
            "cls": "GEDTM30",
            "category": "Topography",
            "desc": "Global 1-Arc-Second DTM (OpenLandMap)",
            "agency": "OpenLandMap",
            "tags": ["gedtm30", "dtm", "global", "30m", "land"],
            "region": "Global",
            "resolution": "1 arc-second (~30m)",
            "license": "CC-BY / Public",
            "urls": {"home": "https://github.com/openlandmap/GEDTM30"},
        },
        "nasadem": {
            "mod": "fetchez.modules.nasadem",
            "cls": "NASADEM",
            "category": "Topography",
            "desc": "NASADEM (Modernized SRTM)",
            "agency": "NASA",
            "tags": ["nasadem", "srtm", "elevation", "global", "30m"],
            "region": "Global (60N to 56S)",
            "resolution": "1 arc-second (~30m)",
            "license": "Public Domain",
            "urls": {"home": "https://earthdata.nasa.gov/"},
        },
        "arcticdem": {
            "mod": "fetchez.modules.arcticdem",
            "cls": "ArcticDEM",
            "category": "Topography",
            "desc": "ArcticDEM (Polar Geospatial Center)",
            "agency": "PGC / NGA",
            "tags": ["arctic", "dem", "topography", "elevation", "polar"],
            "region": "Arctic",
            "resolution": "2m, 10m, 32m",
            "license": "Public Domain",
            "urls": {"home": "https://www.pgc.umn.edu/data/arcticdem/"},
        },
        "tiger": {
            "mod": "fetchez.modules.tiger",
            "cls": "Tiger",
            "category": "Reference",
            "desc": "US Census Bureau TIGER (Boundaries)",
            "agency": "US Census Bureau",
            "tags": ["census", "tiger", "boundaries", "states", "counties", "tracts"],
            "region": "USA",
            "resolution": "Vector",
            "license": "Public Domain",
            "urls": {"home": "https://tigerweb.geo.census.gov/"},
        },
        "ngs": {
            "mod": "fetchez.modules.ngs",
            "cls": "NGS",
            "category": "Geodesy",
            "desc": "National Geodetic Survey (NGS) Monuments",
            "agency": "NOAA",
            "tags": ["ngs", "monuments", "survey", "benchmarks", "geodesy", "control"],
            "region": "USA",
            "resolution": "Point Data",
            "license": "Public Domain",
            "urls": {"home": "https://geodesy.noaa.gov/"},
        },
        "hydrolakes": {
            "mod": "fetchez.modules.hydrolakes",
            "cls": "HydroLAKES",
            "category": "Hydrography",
            "desc": "HydroLAKES Global Shoreline Polygons",
            "agency": "HydroSHEDS",
            "tags": ["lakes", "hydrography", "polygons", "global", "hydrosheds"],
            "region": "Global",
            "resolution": "Vector",
            "license": "CC-BY 4.0",
            "urls": {"home": "https://www.hydrosheds.org/products/hydrolakes"},
        },
        "globathy": {
            "mod": "fetchez.modules.globathy",
            "cls": "GLOBathy",
            "category": "Bathymetry",
            "desc": "GLOBathy (Global Lake Bathymetry Parameters)",
            "agency": "HydroSHEDS / Figshare",
            "tags": ["lakes", "bathymetry", "depth", "volume", "global"],
            "region": "Global",
            "resolution": "Derived Parameters",
            "license": "CC-BY 4.0",
            "urls": {
                "home": "https://figshare.com/articles/dataset/GLOBathy_Bathymetric_Data/13353392"
            },
        },
        "usiei": {
            "mod": "fetchez.modules.usiei",
            "cls": "USIEI",
            "category": "Reference",
            "desc": "US Interagency Elevation Inventory (Metadata Only)",
            "agency": "NOAA / USGS / FEMA",
            "tags": ["inventory", "metadata", "usiei", "lidar", "footprints"],
            "region": "USA",
            "resolution": "Vector Footprints",
            "license": "Public Domain",
            "urls": {"home": "https://coast.noaa.gov/inventory/"},
        },
        # Proj CDN
        "proj": {
            "mod": "fetchez.modules.proj",
            "cls": "PROJ",
            "category": "Geodesy",
            "desc": "PROJ CDN Transformation Grids (Geoids, Shift Grids)",
            "agency": "PROJ / NOAA / NGA",
            "tags": [
                "proj",
                "cdn",
                "geoid",
                "egm2008",
                "vertcon",
                "nadcon",
                "transformation",
            ],
            "region": "Global",
            "resolution": "Various",
            "license": "Public Domain / CC0",
            "urls": {"home": "https://cdn.proj.org/"},
            "aliases": ["geoid", "vertcon"],
        },
        # NOAA VDATUM Tidal Transformations
        "vdatum": {
            "mod": "fetchez.modules.vdatum",
            "cls": "VDatum",
            "category": "Geodesy",
            "desc": "NOAA VDatum Tidal Grids (MLLW, MHHW)",
            "agency": "NOAA",
            "tags": [
                "vdatum",
                "tidal",
                "mllw",
                "mhhw",
                "noaa",
                "vertical-datum",
                "transformation",
            ],
            "region": "USA / Coastal",
            "resolution": "Varies (Regional Grids)",
            "license": "Public Domain",
            "urls": {"home": "https://vdatum.noaa.gov/"},
            "aliases": ["tidal_grids"],
        },
        # DTU Global Models
        "dtu": {
            "mod": "fetchez.modules.dtu",
            "cls": "DTU",
            "category": "Geodesy",
            "desc": "DTU Global Gravity, Altimetry, and Tide Models",
            "agency": "DTU Space",
            "tags": [
                "gravity",
                "altimetry",
                "oceanography",
                "mss",
                "tides",
                "dtu",
                "global",
            ],
            "region": "Global",
            "resolution": "1 arc-minute (~2km)",
            "license": "Public / Scientific Use",
            "urls": {
                "home": "https://ftp.space.dtu.dk/pub/DTU10/",
                "data": "https://ftp.space.dtu.dk/pub/",
            },
        },
        "seanoe": {
            "mod": "fetchez.modules.seanoe",
            "cls": "Seanoe",
            "category": "Geodesy",
            "desc": "FES Global Gravity, Altimetry, and Tide Models",
            "agency": "SHOM",
            "tags": ["tides", "lat", "msl", "fes2014", "global", "open-science"],
            "region": "Global",
            "resolution": "1 arc-minute (~2km)",
            "license": "Public / Scientific Use",
            "urls": {
                "home": "https://www.seanoe.org/data/00742/85408/",
            },
        },
        "osm": {
            "mod": "fetchez.modules.osm",
            "cls": "OSM",
            "category": "Vector",
            "desc": "OpenStreetMap (Coastlines, Water, Buildings)",
            "agency": "OpenStreetMap Foundation",
            "tags": ["osm", "vector", "coastline", "buildings", "roads", "overpass"],
            "region": "Global",
            "resolution": "Vector",
            "license": "ODbL (Open Data Commons Open Database License)",
            "urls": {"home": "https://www.openstreetmap.org/"},
        },
        "wikigeo": {
            "mod": "fetchez.modules.wikigeo",
            "cls": "WikiGeo",
            "category": "Context",
            "desc": "Geolocated Wikipedia Articles (Points of Interest)",
            "agency": "Wikipedia Foundation",
            "tags": ["wikipedia", "context", "labels", "history", "poi", "geosearch"],
            "region": "Global",
            "resolution": "Point Data",
            "license": "CC-BY-SA 3.0",
            "urls": {
                "home": "https://www.wikipedia.org/",
                "api": "https://en.wikipedia.org/w/api.php",
            },
        },
        # USNO Sun/Moon Data
        "sun_moon": {
            "mod": "fetchez.modules.sun_moon",
            "cls": "SunMoon",
            "category": "Reference",
            "desc": "USNO Sun/Moon Rise/Set Times & Phases",
            "agency": "US Naval Observatory",
            "tags": [
                "sun",
                "moon",
                "ephemeris",
                "daylight",
                "planning",
                "astronomy",
                "usno",
            ],
            "region": "Global",
            "resolution": "Temporal (Daily)",
            "license": "Public Domain",
            "urls": {
                "home": "https://aa.usno.navy.mil/data/api",
                "docs": "https://aa.usno.navy.mil/data/docs/RS_OneDay.php",
            },
        },
        # STAC (SpatioTemporal Asset Catalog)
        "stac": {
            "mod": "globato.modules.common.stac",
            "cls": "STACModule",
            "category": "Generic",
            "desc": "Generic STAC API Fetcher (SpatioTemporal Asset Catalog)",
            "agency": "Various",
            "tags": [
                "stac",
                "api",
                "catalog",
                "spatiotemporal",
                "json",
                "cloud-native",
            ],
            "region": "Global",
            "resolution": "Variable",
            "license": "Varies",
            "urls": {
                "spec": "https://stacspec.org/",
                "browser": "https://radiantearth.github.io/stac-browser/",
            },
        },
        # Maxar Open Data
        "maxar": {
            "mod": "globato.modules.maxar",
            "cls": "MaxarOpenData",
            "category": "Imagery",
            "desc": "Maxar Open Data Program (Disaster Response Imagery)",
            "agency": "Maxar Technologies",
            "tags": [
                "maxar",
                "imagery",
                "disaster",
                "response",
                "stac",
                "cog",
                "optical",
                "earthquake",
                "flood",
            ],
            "region": "Global (Event Based)",
            "resolution": "High-Res (~30cm - 50cm)",
            "license": "CC BY-NC 4.0",
            "urls": {
                "home": "https://www.maxar.com/open-data",
                "registry": "https://registry.opendata.aws/maxar-open-data/",
                "license": "https://www.maxar.com/open-data/attribution",
            },
        },
        # The following modules don't need a `region`,
        # they populate `FetchModule.results` in some other way.
        # Cpt-City
        "cpt_city": {
            "mod": "fetchez.modules.cptcity",
            "cls": "CPTCity",
            "category": "Visualization",
            "desc": "Color Palette Tables (CPT) from CPT City",
            "agency": "SeaView Sensing / CPT City",
            "tags": ["visualization", "color", "palette", "cpt", "cartography"],
            "region": "Global",
            "resolution": "N/A",
            "license": "Varies (Mostly Public/Open)",
            "urls": {"home": "http://soliton.vm.bytemark.co.uk/pub/cpt-city/"},
        },
        # Nominatum
        "nominatim": {
            "mod": "fetchez.modules.nominatim",
            "cls": "Nominatim",
            "category": "Reference",
            "desc": "Geocoding service using OpenStreetMap data",
            "agency": "OpenStreetMap Foundation",
            "tags": ["geocode", "osm", "search", "coordinates", "place"],
            "region": "Global",
            "resolution": "N/A",
            "license": "ODbL (Open Data Commons Open Database License)",
            "urls": {
                "home": "https://nominatim.org/",
                "policy": "https://operations.osmfoundation.org/policies/nominatim/",
            },
        },
        "ipinfo": {
            "mod": "fetchez.modules.ipinfo",
            "cls": "IPInfo",
            "category": "Reference",
            "desc": "Query IP Addresses for coordinates",
            "tags": ["utility", "geolocation", "ip", "context"],
            "region": "Custom",
            "resolution": "N/A",
        },
    }

    @classmethod
    def get_info(cls, mod_key: str) -> dict:
        """Retrieve the full metadata dictionary for a module,
        resolving metadta inheritance.
        """

        if mod_key not in cls._modules:
            for k, v in cast(Dict[Any, Any], cls._modules.items()):
                if mod_key in v.get("aliases", []):
                    mod_key = k
                    break

        if mod_key not in cls._modules:
            return {}

        entry: Dict[Any, Any] = cast(Dict[Any, Any], cls._modules[mod_key])

        if "inherits" in entry:
            parent_key = entry["inherits"]
            parent = cls.get_info(parent_key)

            merged = copy.deepcopy(parent)

            for k, v in entry.items():
                if k == "tags" and "tags" in merged:
                    merged["tags"] = list(set(merged["tags"] + v))
                elif k == "urls" and "urls" in merged:
                    merged["urls"].update(v)
                else:
                    merged[k] = v

            return merged

        return entry

    @classmethod
    def load_module(cls, mod_key):
        """Import and return the (module or user-plugin) class using `importlib`."""

        meta = cls._modules[mod_key]

        # User Plugin
        if "_class_obj" in meta:
            return meta["_class_obj"]

        # Standard Module
        if mod_key not in cls._modules:
            return None

        info = cls.get_info(mod_key)
        if not info:
            return None

        try:
            module = importlib.import_module(info["mod"])
            mod_cls = getattr(module, info["cls"])
            return mod_cls
        except (ImportError, AttributeError) as e:
            logger.error(f"Failed to load {mod_key}: {e}")
            return None

    @classmethod
    def load_user_plugins(cls):
        """Scan ~/.fetchez/plugins/ and .fetchez/plugins for external modules and register them."""

        import os
        import sys
        import inspect
        import importlib.util
        from . import core

        home_dir = os.path.expanduser("~")
        home_plugin_dir = os.path.join(home_dir, ".fetchez", "plugins")
        cwd_plugin_dir = os.path.join(".fetchez", "plugins")

        for plugin_dir in [home_plugin_dir, cwd_plugin_dir]:
            if not os.path.exists(plugin_dir):
                return

            # Add the plugin_dir to the system path for imports
            sys.path.insert(0, plugin_dir)

            for filename in os.listdir(plugin_dir):
                if filename.endswith(".py") and not filename.startswith("_"):
                    filepath = os.path.join(plugin_dir, filename)
                    module_name = f"user_plugin_{filename[:-3]}"
                    try:
                        spec = importlib.util.spec_from_file_location(
                            module_name, filepath
                        )
                        if spec and spec.loader:
                            user_mod = importlib.util.module_from_spec(spec)
                            spec.loader.exec_module(user_mod)

                            for name, obj in inspect.getmembers(user_mod):
                                if inspect.isclass(obj) and issubclass(
                                    obj, core.FetchModule
                                ):
                                    if obj is core.FetchModule:
                                        continue

                                    # Check for @cli_opts metadata; or defaults
                                    mod_key = getattr(obj, "name", name.lower())
                                    logger.info(f"Loaded user plugin: {mod_key}")

                                    cls._modules[mod_key] = {
                                        "mod": f"user_plugin_{filename[:-3]}",
                                        "cls": name,
                                        "category": "User Plugin",
                                        "desc": getattr(
                                            obj, "__doc__", "User defined module"
                                        )
                                        .strip()
                                        .split("\n")[0],
                                        "agency": "External",
                                        "_class_obj": obj,
                                    }

                    except Exception as e:
                        logger.warning(f"Failed to load plugin {filename}: {e}")

            # Remove the plugin_dir from the system path
            sys.path.pop(0)

    @classmethod
    def load_installed_plugins(cls):
        """Load plugins installed via Pip (entry_points)."""

        # Look for any installed package that registered a 'fetchez.plugins' entry point
        entry_points = importlib.metadata.entry_points(group="fetchez.plugins")
        for entry_point in entry_points:
            try:
                plugin_module = entry_point.load()
                # The plugin module should expose a 'setup_fetchez' function
                if hasattr(plugin_module, "setup_fetchez"):
                    plugin_module.setup_fetchez(cls)  # Pass the Registry to the plugin
                    logger.info(f"Loaded extension: {entry_point.name}")
            except Exception as e:
                logger.error(f"Failed to load extension {entry_point.name}: {e}")

    @classmethod
    def search_modules(cls, query: str) -> list:
        """Search modules by matching the query string against:
        Name, Description, Agency, Tags, License, Category, and Aliases.
        """

        query = query.lower()
        matches = []

        for key in cls._modules.keys():
            meta = cls.get_info(key)

            searchable_text = [
                key,
                meta.get("desc", ""),
                meta.get("agency", ""),
                meta.get("category", ""),
                meta.get("license", ""),
            ]

            searchable_text.extend(meta.get("tags", []))
            searchable_text.extend(meta.get("aliases", []))

            if any(query in s.lower() for s in searchable_text):
                matches.append(key)

        return sorted(matches)

    @classmethod
    def register_module(cls, mod_key, mod_cls, metadata=None):
        """Register a new module dynamically (e.g., from a plugin).

        Args:
            mod_key (str): The CLI command name (e.g., 'datum_grids').
            mod_cls: The actual Python class (inheriting from FetchModule).
            metadata (dict): Optional metadata for discovery (desc, tags, etc.).
        """

        if metadata is None:
            metadata = {}

        # Build the registry entry
        entry = {
            "mod": mod_cls.__module__,  # The python path (e.g. 'transformez.modules')
            "cls": mod_cls.__name__,  # The class name (e.g. 'DatumGridFetcher')
            "_class_obj": mod_cls,  # Direct reference (skips importlib later)
            "category": metadata.get("category", "User Plugin"),
            "desc": metadata.get("desc", "No description provided."),
            "tags": metadata.get("tags", []),
            "agency": metadata.get("agency", "External"),
            "license": metadata.get("license", "Unknown"),
        }

        cls._modules[mod_key] = entry
        logger.debug(f"Registered external module: {mod_key}")
