""".. title:: Climodat Station Climatology

Return to `API Services </api/#json>`_

Documentation for /json/climodat_stclimo.py
-------------------------------------------

This provides computed climatologies based on period of record data.

Changelog
---------

- 2024-08-05: Initial documtation update

Example Requests
----------------

Provide the computed climatology for Ames, IA

https://mesonet.agron.iastate.edu/json/climodat_stclimo.py?station=IATAME

"""

import json
from datetime import datetime
from typing import Annotated

from pydantic import Field
from pyiem.database import sql_helper, with_sqlalchemy_conn
from pyiem.webutil import CGIModel, iemapp
from sqlalchemy.engine import Connection

from iemweb.fields import CALLBACK_FIELD
from iemweb.util import json_response_dict


class Schema(CGIModel):
    """See how we are called."""

    callback: CALLBACK_FIELD = None
    eyear: Annotated[int, Field(description="End Year")] = (
        datetime.now().year + 1
    )
    station: Annotated[
        str, Field(description="Station Identifier", max_length=6)
    ] = "IA0200"
    syear: Annotated[int, Field(description="Start Year")] = 1800


@with_sqlalchemy_conn("coop")
def run(query: Schema, conn: Connection | None = None) -> dict:
    """Generate the data. Note, we have API users of this..."""
    res = conn.execute(
        sql_helper("""
    WITH data as (
      SELECT sday, year, precip,
      avg(precip) OVER (PARTITION by sday) as avg_precip,
      high, rank() OVER (PARTITION by sday ORDER by high DESC nulls last)
        as max_high,
      avg(high) OVER (PARTITION by sday) as avg_high,
      rank() OVER (PARTITION by sday ORDER by high ASC nulls last)
        as min_high,
      low, rank() OVER (PARTITION by sday ORDER by low DESC nulls last)
        as max_low,
      avg(low) OVER (PARTITION by sday) as avg_low,
      rank() OVER (PARTITION by sday ORDER by low ASC nulls last)
        as min_low,
      rank() OVER (PARTITION by sday ORDER by precip DESC nulls last)
        as max_precip,
      max(high - low) OVER (PARTITION by sday) as max_range,
      min(high - low) OVER (PARTITION by sday) as min_range
      from alldata
    WHERE station = :station and year >= :syear and year < :eyear),

    max_highs as (
      SELECT sday, high, array_agg(year) as years from data
      where max_high = 1 GROUP by sday, high),
    min_highs as (
      SELECT sday, high, array_agg(year) as years from data
      where min_high = 1 GROUP by sday, high),

    max_lows as (
      SELECT sday, low, array_agg(year) as years from data
      where max_low = 1 GROUP by sday, low),
    min_lows as (
      SELECT sday, low, array_agg(year) as years from data
      where min_low = 1 GROUP by sday, low),

    max_precip as (
      SELECT sday, precip, array_agg(year) as years from data
      where max_precip = 1 GROUP by sday, precip),

    avgs as (
      SELECT sday, count(*) as cnt, max(avg_precip) as p,
      max(max_range) as max_range, min(min_range) as min_range,
      max(avg_high) as h, max(avg_low) as l from data GROUP by sday)

    SELECT a.sday, a.cnt,
    a.h, xh.high as xh_high, xh.years as xh_years,
    nh.high as nh_high, nh.years as nh_years,
    a.l, xl.low as xl_low, xl.years as xl_years,
    nl.low as nl_low, nl.years as nl_years,
    a.p, mp.precip, mp.years as mp_years, a.max_range, a.min_range
    from avgs a, max_highs xh, min_highs nh, max_lows xl, min_lows nl,
    max_precip mp
    WHERE xh.sday = a.sday and xh.sday = nh.sday and xh.sday = xl.sday and
    xh.sday = nl.sday and xh.sday = mp.sday ORDER by sday ASC
    """),
        {"station": query.station, "syear": query.syear, "eyear": query.eyear},
    )
    data = json_response_dict(
        {
            "station": query.station,
            "start_year": query.syear,
            "end_year": query.eyear,
            "climatology": [],
        }
    )
    for row in res.mappings():
        data["climatology"].append(
            dict(
                month=int(row["sday"][:2]),
                day=int(row["sday"][2:]),
                years=row["cnt"],
                avg_high=None if row["h"] is None else float(row["h"]),
                max_high=row["xh_high"],
                max_high_years=row["xh_years"],
                min_high=row["nh_high"],
                min_high_years=row["nh_years"],
                avg_low=None if row["l"] is None else float(row["l"]),
                max_low=row["xl_low"],
                max_low_years=row["xl_years"],
                min_low=row["nl_low"],
                min_low_years=row["nl_years"],
                avg_precip=None if row["p"] is None else float(row["p"]),
                max_precip=row["precip"],
                max_precip_years=row["mp_years"],
                max_range=row["max_range"],
                min_range=row["min_range"],
            )
        )
    return data


def get_key(environ):
    """Figure out the cache key"""
    return (
        f"/json/climodat_stclimo/{environ['station']}/"
        f"{environ['syear']}/{environ['eyear']}"
    )


@iemapp(
    help=__doc__,
    schema=Schema,
    memcachekey=get_key,
)
def application(environ, start_response):
    """Answer request."""
    query: Schema = environ["_cgimodel_schema"]

    payload = json.dumps(run(query))
    start_response("200 OK", [("Content-type", "application/json")])
    return payload
