"""TMB Day 1 geometric terrain-shadow prototype using an explicitly supplied DEM."""

import argparse
import csv
import hashlib
import json
from dataclasses import asdict
from datetime import timezone
from pathlib import Path
from zoneinfo import ZoneInfoNotFoundError

from pyproj.exceptions import ProjError

from mountain_twin.exposure import analyze, load_day1, resolve_local_time
from mountain_twin.terrain.horizon import classify, compare_elevations, horizon_profile


def run(points, instant, dem, step_m=30, max_distance_m=5000, observer_height_m=0):
    samples = [dem.sample(p.latitude, p.longitude) for p in points]
    rows = []
    for point, solar, sample in zip(points, analyze(points, instant), samples):
        horizon = horizon_profile(
            dem,
            point.latitude,
            point.longitude,
            solar.solar_azimuth_deg,
            step_m,
            max_distance_m,
            observer_height_m,
        )
        row = asdict(solar)
        row.update(asdict(horizon))
        row.update(
            dem_elevation_m=sample.elevation_m,
            dem_sample_status=sample.status,
            dem_minus_gpx_m=(
                sample.elevation_m - point.elevation_m
                if sample.elevation_m is not None and point.elevation_m is not None
                else None
            ),
            sampling_step_m=step_m,
            max_distance_m=max_distance_m,
            observer_height_m=observer_height_m,
        )
        row["terrain_shadow_status"] = classify(solar.solar_elevation_deg, horizon)
        rows.append(row)
    return rows, compare_elevations(points, samples)


def main(argv=None):
    from rasterio.errors import RasterioError

    from mountain_twin.terrain.dem import GeoTiffDEM

    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--dem", required=True, type=Path)
    parser.add_argument("--dem-metadata", required=True, type=Path)
    parser.add_argument(
        "--input", type=Path, default=Path("data/generated/trail_points_master.csv")
    )
    parser.add_argument("--date", default="2026-07-02")
    parser.add_argument("--time", required=True)
    parser.add_argument("--timezone", default="Europe/Paris")
    parser.add_argument("--fold", type=int, choices=(0, 1))
    parser.add_argument("--step-m", type=float, default=30)
    parser.add_argument("--max-distance-m", type=float, default=5000)
    parser.add_argument("--observer-height-m", type=float, default=0)
    parser.add_argument("--output", type=Path)
    args = parser.parse_args(argv)
    try:
        instant = resolve_local_time(f"{args.date}T{args.time}", args.timezone, args.fold)
        root = Path("data/generated/terrain_shadow").resolve()
        output = args.output or root / (
            f"tmb_day_01_{instant.astimezone(timezone.utc):%Y%m%dT%H%M%S%fZ}.csv"
        )
        report_path = output.with_suffix(".json")
        if root not in output.resolve().parents or output.suffix != ".csv":
            raise ValueError("output must be a CSV under data/generated/terrain_shadow/")
        if output.exists() or report_path.exists():
            raise ValueError("output CSV or report already exists; choose a new output")
        metadata = json.loads(args.dem_metadata.read_text(encoding="utf-8"))
        if not isinstance(metadata, dict):
            raise ValueError("DEM metadata must be a JSON object")
        points = load_day1(args.input)
        with GeoTiffDEM(args.dem, metadata) as dem:
            rows, metrics = run(
                points, instant, dem, args.step_m, args.max_distance_m, args.observer_height_m
            )
            report = dict(
                dem=dem.metadata,
                provider=asdict(dem.info),
                elevation_comparison=metrics,
                route_input_sha256=hashlib.sha256(args.input.read_bytes()).hexdigest(),
                analysis_time_local=instant.isoformat(),
                timezone=args.timezone,
                step_m=args.step_m,
                max_distance_m=args.max_distance_m,
                observer_height_m=args.observer_height_m,
                model="geometric finite-range nearest-cell rays; no curvature/refraction",
            )
        payload = json.dumps(report, indent=2, sort_keys=True, allow_nan=False) + "\n"
        output.parent.mkdir(parents=True, exist_ok=True)
        with output.open("x", newline="", encoding="utf-8") as handle:
            writer = csv.DictWriter(handle, fieldnames=list(rows[0]), lineterminator="\n")
            writer.writeheader()
            writer.writerows(rows)
        with report_path.open("x", encoding="utf-8") as handle:
            handle.write(payload)
        print(payload, end="")
        print(f"Wrote {len(rows)} points to {output}; scientific accuracy unvalidated.")
        return 0
    except (
        OSError,
        ValueError,
        KeyError,
        TypeError,
        ZoneInfoNotFoundError,
        ProjError,
        RasterioError,
    ) as exc:
        print(f"ERROR: {exc}")
        return 2


if __name__ == "__main__":
    raise SystemExit(main())
