"""..title:: Tool to debug NWS LAT...LON polygons

Simple tool to help debug NWS LAT...LON polygons.

Changelog
---------

- 2025-11-10: Added option to specify that the given longitudes are east. This
  is a painful NWS quirk for products from Guam.
- 2025-01-03: Updated to use pydantic for input validation

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

The urlencoding here is a bit ugly, but alas.

https://mesonet.agron.iastate.edu/nws/debug_latlon/generate_plot.py?\
text=LAT...LON%203920%208402%203920%208400%203918%208400%203918%208402%20%0A\
&title=Test

Another example, but with east longitude enabled

https://mesonet.agron.iastate.edu/nws/debug_latlon/generate_plot.py?\
text=LAT...LON%203920%208402%203920%208400%203918%208400%203918%208402%20%0A\
&title=Test&east=1

"""

import json
import os
import tempfile
from typing import Annotated

import geopandas as gpd
import matplotlib.patheffects as PathEffects
from pydantic import Field
from pyiem.nws.product import str2polygon
from pyiem.plot import fitbox
from pyiem.plot.use_agg import figure
from pyiem.reference import ISO8601, TWITTER_RESOLUTION_INCH
from pyiem.util import utc
from pyiem.webutil import CGIModel, iemapp
from shapely.geometry import Polygon


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

    east: Annotated[
        bool,
        Field(
            description="Interpret given longitudes as East Longitudes (Guam)"
        ),
    ] = False
    text: Annotated[str, Field(description="The text to parse", min_length=1)]
    title: Annotated[
        str | None, Field(description="Optional title for the plot")
    ] = None


def plot_poly(fig, poly, environ: dict):
    """Add to axes."""
    ax = fig.add_axes((0.15, 0.15, 0.7, 0.7))
    title = environ["title"] or "No Title Provided"
    title = title[:120]
    title += (
        f"\npolygon.is_valid: {poly.is_valid} "
        f"linearring.is_valid: {poly.exterior.is_valid}"
    )
    fitbox(fig, title, 0.15, 0.9, 0.9, 0.95)

    X, Y = poly.exterior.xy
    for i in range(len(X) - 1):
        ax.arrow(
            X[i],
            Y[i],
            X[i + 1] - X[i],
            Y[i + 1] - Y[i],
            head_width=0.006,
            width=0.001,
            length_includes_head=True,
            color="skyblue",
        )
        txt = ax.text(
            X[i],
            Y[i],
            str(i + 1),
            va="center",
            ha="center",
            color="red",
            fontsize=20,
        )
        txt.set_path_effects(
            [PathEffects.withStroke(linewidth=2, foreground="white")]
        )
    ax.set_xlim(min(X) - 0.02, max(X) + 0.02)
    ax.set_ylim(min(Y) - 0.02, max(Y) + 0.02)
    ax.set_ylabel("Latitude [°N]", fontsize=18)
    ax.set_xlabel("Longitude [°E]", fontsize=18)
    ax.grid(True)
    for tick in ax.get_xticklabels():
        tick.set_rotation(45)


def process(environ: dict):
    """Make something pretty."""
    text = environ.get("text")
    text = text.replace("LAT...LON", "").replace("\n", " ")
    # Add extra whitespace to account for upstream issues
    poly = str2polygon(text + " \n")
    if environ["east"]:
        newpoints = [[0 - pt[0], pt[1]] for pt in poly.exterior.coords]
        poly = Polygon(newpoints)
    fig = figure(figsize=TWITTER_RESOLUTION_INCH)

    res = {}
    if poly is not None:
        plot_poly(fig, poly, environ)
        res["geojson"] = gpd.GeoSeries([poly]).__geo_interface__

    fig.text(0.01, 0.01, f"Generated: {utc().strftime(ISO8601)}")
    with tempfile.NamedTemporaryFile() as tmpfd:
        name = os.path.basename(tmpfd.name)
        fig.savefig(f"/var/webtmp/{name}.png")
    res["imgurl"] = f"/tmp/{name}.png"
    return res


@iemapp(help=__doc__, schema=Schema)
def application(environ: dict, start_response: callable):
    """The App."""
    res = process(environ)
    headers = [("Content-type", "application/json")]
    start_response("200 OK", headers)
    return json.dumps(res)
