#!/usr/bin/env python3
"""
Build county-level average diurnal temperature range from NOAA nClimGrid-Daily.

The output is an ETL artifact used to keep climate-data.csv lean:

data/noaa/county_diurnal_temperature_range.csv

Metric definition:
- avgDiurnalTempRangeF: mean daily county Tmax - Tmin, expressed as a
  Fahrenheit temperature difference, over the selected analysis period.
"""

from __future__ import annotations

import argparse
import csv
from dataclasses import dataclass
from pathlib import Path

from build_county_locally_extreme_data import (
    DEFAULT_STATE_CROSSWALK,
    DEFAULT_TMAX_DIR,
    DEFAULT_TMIN_DIR,
    load_state_crosswalk,
    require_monthly_file,
    rows_by_fips,
)


REPO_ROOT = Path(__file__).resolve().parents[1]
DEFAULT_OUT = REPO_ROOT / "data" / "noaa" / "county_diurnal_temperature_range.csv"
SOURCE_TAG = "noaa-nclimgrid-daily-county-area-averages-scaled"


@dataclass
class DiurnalRangeTotals:
    """Running total for one county's daily Tmax-Tmin differences."""

    noaa_region_code: str = ""
    county_name: str = ""
    total_range_c: float = 0.0
    valid_days: int = 0
    skipped_negative_days: int = 0


def c_delta_to_f_delta(value_c: float) -> float:
    """Convert a Celsius temperature difference to a Fahrenheit difference."""
    return value_c * 9.0 / 5.0


def build_diurnal_temperature_range(
    *,
    start_year: int,
    end_year: int,
    tmax_dir: Path,
    tmin_dir: Path,
    state_crosswalk_path: Path,
) -> dict[str, DiurnalRangeTotals]:
    """Average daily county Tmax-Tmin over the requested analysis years."""
    state_crosswalk = load_state_crosswalk(state_crosswalk_path)
    totals: dict[str, DiurnalRangeTotals] = {}

    for year in range(start_year, end_year + 1):
        print(f"Reading daily Tmax/Tmin for {year}...")
        for month in range(1, 13):
            tmax_path = require_monthly_file(tmax_dir, "tmax", year, month)
            tmin_path = require_monthly_file(tmin_dir, "tmin", year, month)
            tmax_rows = rows_by_fips(
                tmax_path,
                year=year,
                month=month,
                state_crosswalk=state_crosswalk,
            )
            tmin_rows = rows_by_fips(
                tmin_path,
                year=year,
                month=month,
                state_crosswalk=state_crosswalk,
            )

            for county_fips in sorted(set(tmax_rows) & set(tmin_rows)):
                tmax_record = tmax_rows[county_fips]
                tmin_record = tmin_rows[county_fips]
                county_totals = totals.setdefault(
                    county_fips,
                    DiurnalRangeTotals(
                        noaa_region_code=tmax_record.region_code,
                        county_name=tmax_record.county_name,
                    ),
                )

                for tmax_value, tmin_value in zip(tmax_record.values, tmin_record.values):
                    if tmax_value is None or tmin_value is None:
                        continue
                    daily_range_c = tmax_value - tmin_value
                    if daily_range_c < 0:
                        county_totals.skipped_negative_days += 1
                        continue
                    county_totals.total_range_c += daily_range_c
                    county_totals.valid_days += 1

    return totals


def write_diurnal_temperature_range_csv(
    *,
    path: Path,
    totals: dict[str, DiurnalRangeTotals],
    start_year: int,
    end_year: int,
) -> None:
    """Write county DTR summary rows."""
    path.parent.mkdir(parents=True, exist_ok=True)
    fieldnames = [
        "countyFips",
        "noaaRegionCode",
        "countyName",
        "avgDiurnalTempRangeC",
        "avgDiurnalTempRangeF",
        "validDays",
        "skippedNegativeDays",
        "analysisStartYear",
        "analysisEndYear",
        "source",
    ]

    with path.open("w", encoding="utf-8", newline="") as csv_file:
        writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
        writer.writeheader()
        for county_fips in sorted(totals):
            county_totals = totals[county_fips]
            avg_range_c = (
                county_totals.total_range_c / county_totals.valid_days
                if county_totals.valid_days
                else None
            )
            writer.writerow(
                {
                    "countyFips": county_fips,
                    "noaaRegionCode": county_totals.noaa_region_code,
                    "countyName": county_totals.county_name,
                    "avgDiurnalTempRangeC": f"{avg_range_c:.2f}" if avg_range_c is not None else "",
                    "avgDiurnalTempRangeF": (
                        f"{c_delta_to_f_delta(avg_range_c):.2f}" if avg_range_c is not None else ""
                    ),
                    "validDays": county_totals.valid_days,
                    "skippedNegativeDays": county_totals.skipped_negative_days,
                    "analysisStartYear": start_year,
                    "analysisEndYear": end_year,
                    "source": SOURCE_TAG,
                }
            )

    print(f"Wrote {len(totals)} county DTR rows to {path}")


def parse_args() -> argparse.Namespace:
    """Parse command-line arguments."""
    parser = argparse.ArgumentParser(
        description="Build county average diurnal temperature range from cached NOAA Tmax/Tmin files."
    )
    parser.add_argument("--start-year", type=int, default=1991)
    parser.add_argument("--end-year", type=int, default=2020)
    parser.add_argument("--tmax-dir", type=Path, default=DEFAULT_TMAX_DIR)
    parser.add_argument("--tmin-dir", type=Path, default=DEFAULT_TMIN_DIR)
    parser.add_argument("--state-crosswalk", type=Path, default=DEFAULT_STATE_CROSSWALK)
    parser.add_argument("--out", type=Path, default=DEFAULT_OUT)
    return parser.parse_args()


def main() -> None:
    """Run the DTR ETL."""
    args = parse_args()
    totals = build_diurnal_temperature_range(
        start_year=args.start_year,
        end_year=args.end_year,
        tmax_dir=args.tmax_dir,
        tmin_dir=args.tmin_dir,
        state_crosswalk_path=args.state_crosswalk,
    )
    write_diurnal_temperature_range_csv(
        path=args.out,
        totals=totals,
        start_year=args.start_year,
        end_year=args.end_year,
    )


if __name__ == "__main__":
    main()
