"""Offline test for kalshi_weather_signals.py: fake HTTP only. Never touches the real API.
python test_kalshi_signals.py"""
import csv, json, os, tempfile
from datetime import datetime, timezone
os.environ["GETFIXN_KEY"] = "tfx_live_TESTKEY_NOT_REAL"
import gf_client as gf, kalshi_weather_signals as ks

class R:
    def __init__(s, code, data=None): s.status_code, s._d, s.headers, s.text = code, data or {}, {}, json.dumps(data or {})
    def json(s): return s._d

class Fake:
    def __init__(s, script): s.script, s.calls = list(script), []
    def post(s, url, json=None, headers=None, timeout=None):
        s.calls.append((url, json)); return s.script.pop(0)

gf.SLEEP = lambda s: None
LOCS = [
    {"label": "Hot", "lat": 30.0, "lon": -97.0, "tz": "America/Chicago",
     "contracts": [{"metric": "temperature_f", "op": "above", "threshold": 90}]},
    {"label": "Cold", "lat": 45.0, "lon": -93.0, "tz": "America/Chicago",
     "contracts": [{"metric": "temperature_f", "op": "below", "threshold": 32},
                   {"metric": "snow_in", "op": "above", "threshold": 1}]},
]
def WE(a, b, extra=None):
    cb = {"temperature_f": b}; cb.update(extra or {})
    return R(200, {"results": [{"current": {"temperature_f": a, "observed_at": "2026-01-05T11:50:00Z"}, "alerts": []},
                               {"current": cb, "alerts": [{"event": "Winter Storm Watch"}]}]})
GR = R(200, {"results": [{"grade": "B"}, {"grade": "E"}]})
T1 = datetime(2026, 1, 5, 12, 0, tzinfo=timezone.utc)

# above/below maths, margin sign, grade score, observation age, missing metric stays None
gf.HTTP = Fake([WE(93.0, 28.0), GR]); st = {}
rows = ks.build(LOCS, st, now=T1)
hot, cold, snow = rows
assert hot["met_now"] is True and hot["margin_now"] == 3.0 and hot["age_minutes"] == 10.0
assert cold["met_now"] is True and cold["margin_now"] == 4.0   # 28 is below 32 by 4
assert snow["value_now"] is None and snow["met_now"] is None   # API had no snow_in: we do not guess
assert hot["grade"] == "B" and cold["grade_score"] == 4
assert len(gf.HTTP.calls) == 2

# running extremes: keeps the day's high, resets on a new local day
T2 = datetime(2026, 1, 5, 18, 0, tzinfo=timezone.utc)
gf.HTTP = Fake([WE(88.0, 20.0), GR]); rows = ks.build(LOCS, st, now=T2)
assert rows[0]["value_now"] == 88.0 and rows[0]["extreme_so_far"] == 93.0 and rows[0]["extreme_margin"] == 3.0
assert rows[1]["extreme_so_far"] == 20.0
T3 = datetime(2026, 1, 6, 18, 0, tzinfo=timezone.utc)
gf.HTTP = Fake([WE(70.0, 40.0), GR]); rows = ks.build(LOCS, st, now=T3)
assert rows[0]["extreme_so_far"] == 70.0 and rows[0]["local_date"] == "2026-01-06"

# grades off: one call only, grade empty
gf.HTTP = Fake([WE(70.0, 40.0)]); rows = ks.build(LOCS, {}, now=T1, grades_on=False)
assert len(gf.HTTP.calls) == 1 and rows[0]["grade"] is None

# only talks to the GetFixn API, only two known routes, never an exchange
assert all(u.startswith("https://data.truefixr.com/v1/") for u, _ in gf.HTTP.calls)
assert ks.estimate_cost(LOCS, True) == 2 * 0.0502 and ks.estimate_cost(LOCS, False) == 2 * 0.0002

# out of money: 402 raises LowBalance and is not retried
gf.HTTP = Fake([R(402)])
try: ks.build(LOCS, {}, now=T1); assert 0
except gf.LowBalance: assert len(gf.HTTP.calls) == 1

# files: json + appended csv, secrets never inside, a different CSV is refused
d = tempfile.mkdtemp(); os.chdir(d)
gf.HTTP = Fake([WE(93.0, 28.0), GR]); rows = ks.build(LOCS, {}, now=T1)
ks.write(rows); ks.write(rows)
assert len(list(csv.DictReader(open("signals.csv")))) == 6
blob = open("signals.csv").read() + open("signals.json").read(); assert "TESTKEY" not in blob
open("other.csv", "w").write("a,b\n1,2\n")
try: ks.write(rows, "other"); assert 0
except SystemExit: pass

# bad input is rejected up front
for bad in ([{"label": "x", "lat": 95, "lon": 0, "contracts": [{"op": "above", "threshold": 1}]}],
            [{"label": "x", "lat": 1, "lon": 0, "contracts": [{"op": "over", "threshold": 1}]}],
            [{"label": "x", "lat": 1, "lon": 0, "tz": "Nowhere/Land", "contracts": [{"op": "above", "threshold": 1}]}],
            [{"label": "x", "lat": 1, "lon": 0, "contracts": []}]):
    json.dump(bad, open("bad.json", "w"))
    try: ks.load("bad.json"); assert 0
    except (ValueError, KeyError): pass
    except Exception as e:
        assert "ZoneInfo" in type(e).__name__ or "KeyError" in type(e).__name__, e
print("all offline tests passed")
