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

"""
fetchez.modules.vdatum
~~~~~~~~~~~~~~~~~~~~~~

Fetch NOAA Tidal Grids (MLLW, MHHW) from vdatum.noaa.gov.

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

import os
import logging
from typing import Optional
from fetchez import core
from fetchez.modules import FetchModule
from fetchez import cli
from fetchez import fred

logger = logging.getLogger(__name__)

VDATUM_DATA_URL = "https://vdatum.noaa.gov/download/data/"
VDATUM_REGIONS = [
    "TIDAL",
    "IGLD85",
    "XGEOID16B",
    "XGEOID17B",
    "XGEOID18B",
    "XGEOID19B",
    "XGEOID20B",
    "VERTCON",
]


@cli.cli_opts(
    help_text="NOAA VDatum Tidal Grids",
    datatype='Filter by datum type (e.g., "mllw", "mhhw", "tidal").',
    update="Force a re-scrape of the NOAA website.",
)
class VDatum(FetchModule):
    name = "vdatum"
    meta_category = "Geodesy"
    meta_desc = "NOAA VDatum Tidal Grids (MLLW, MHHW)"
    meta_agency = "NOAA"
    meta_tags = [
        "vdatum",
        "tidal",
        "mllw",
        "mhhw",
        "noaa",
        "vertical-datum",
        "transformation",
    ]
    meta_region = "USA / Coastal"
    meta_resolution = "Varies (Regional Grids)"
    meta_license = "Public Domain"
    meta_urls = {"home": "https://vdatum.noaa.gov/"}
    meta_aliases = ["tidal_grids"]

    """Fetch NOAA VDatum grids, specifically Tidal Datums (MLLW, MHHW).

    Because these grids are not available in the PROJ CDN, this module
    performs a "heavy" discovery process:

    - Downloads regional ZIP files from NOAA.
    - Inspects internal .inf files to determine bounding boxes.
    - Builds a local spatial index (FRED) for future fast lookups.
    """

    def __init__(self, datatype: Optional[str] = None, update: bool = False, **kwargs):
        super().__init__(name="vdatum", **kwargs)
        self.datatype = datatype.lower() if datatype else None
        self.force_update = update

        self.fred = fred.FRED("vdatum", local=False)

    def _scrape_and_index(self):
        """Download zips, parse .inf, update index."""

        logger.info("Initializing VDatum Index (This may take a moment)...")

        temp_dir = os.path.join(self._outdir, "temp_idx")
        if not os.path.exists(temp_dir):
            os.makedirs(temp_dir)

        for region in VDATUM_REGIONS:
            fname = f"{region}.zip"
            if region == "TIDAL":
                fname = "DEVAemb12_8301.zip"  # Example mapping, may vary
            elif "XGEOID" in region:
                fname = f"vdatum_{region}.zip"

            url = f"{VDATUM_DATA_URL}{fname}"
            local_zip = os.path.join(temp_dir, fname)

            logger.info(f"Indexing {region}...")
            if core.Fetch(url).fetch_file(local_zip) != 0:
                continue

            try:
                import zipfile

                with zipfile.ZipFile(local_zip, "r") as z:
                    for zf in z.namelist():
                        if zf.endswith(".inf"):
                            with z.open(zf) as inf:
                                content = inf.read().decode("utf-8", errors="ignore")
                                meta = self._parse_inf(content)

                                if meta:
                                    geom = {
                                        "type": "Polygon",
                                        "coordinates": [
                                            [
                                                [meta["w"], meta["s"]],
                                                [meta["e"], meta["s"]],
                                                [meta["e"], meta["n"]],
                                                [meta["w"], meta["n"]],
                                                [meta["w"], meta["s"]],
                                            ]
                                        ],
                                    }

                                    self.fred.add_survey(
                                        geom,
                                        Name=zf,
                                        ID=region,
                                        Agency="NOAA",
                                        DataLink=url,
                                        DataType="tidal"
                                        if region == "TIDAL"
                                        else "geoid",
                                        DataSource="vdatum",
                                    )
            except Exception as e:
                logger.warning(f"Failed to parse {fname}: {e}")

            if os.path.exists(local_zip):
                os.remove(local_zip)

        self.fred.save()
        logger.info("VDatum Indexing Complete.")

    def _parse_inf(self, text):
        """Helper to extract bounds from VDatum INF format."""

        d = {}
        for line in text.splitlines():
            if "=" in line:
                k, v = line.split("=", 1)
                d[k.strip().lower().split(".")[-1]] = v.strip()

        try:
            return {
                "w": float(d.get("minlon", 0)),
                "e": float(d.get("maxlon", 0)),
                "s": float(d.get("minlat", 0)),
                "n": float(d.get("maxlat", 0)),
            }
        except Exception:
            return None

    def run(self):
        if self.force_update or not self.fred.features:
            self._scrape_and_index()

        if not self.fred.features:
            logger.error("VDatum index is empty. Scrape failed.")
            return self

        results = self.fred.search(region=self.region)

        for r in results:
            if self.datatype and self.datatype not in r.get("DataType", ""):
                continue

            self.add_entry_to_results(
                url=r["DataLink"],
                dst_fn=os.path.basename(r["DataLink"]),
                data_type=r["DataType"],
                agency="NOAA",
                title=f"VDatum Grid ({r['ID']})",
            )

        return self
