"""voter_instrument_independence_replay.py -- entry 0125.

Re-scores past multi-sensor "voted" fire events under an instrument-independence rule: a vote needs two
different INSTRUMENTS, not two algorithms run on the same image. For each event it also asks whether a
NASA FIRMS (VIIRS/MODIS) pixel lies within 5 km and 12 h -- an outcome check the rule does not see.

Input: a SQLite file with two tables (column names as used by PHOENIX):
  voted(event_id, ts_last_utc, lat_center, lon_center, voter_list_json)   -- voter_list_json = JSON list of family names
  firms(lat, lng, ts)                                                       -- FIRMS pixels, ISO-8601 UTC
Usage:  python voter_instrument_independence_replay.py events.sqlite
"""
import json
import math
import sqlite3
import sys
from datetime import datetime, timedelta

GEO_IR_PREFIXES = ("wind_diff", "subpixel", "fci", "seviri", "dozier", "joint_dozier", "mtg", "w8",
                   "real_bogus", "phoenix_sat_validator", "enhanced")
NEWS_PREFIXES = ("ansa", "ragusanews", "qds", "gds_news", "italian_news", "blogsicilia", "itacanotizie",
                 "ennapress", "siracusanews", "canalesicilia", "livesicilia", "news_")
INSTRUMENT_EXACT = {"firms": "polar_viirs_modis", "viirs_dnb": "polar_viirs_modis",
                    "sentinel2_swir": "sentinel2", "s2_active_fire": "sentinel2", "sentinel1_sar": "sar",
                    "tropomi": "tropomi", "vigili_fuoco": "incident_report"}


def instrument_of(family):
    f = str(family or "").lower().strip()
    if f in INSTRUMENT_EXACT:
        return INSTRUMENT_EXACT[f]
    if f.startswith(GEO_IR_PREFIXES):
        return "geo_ir"          # MTG FCI and MSG SEVIRI: one witness, see entry 0125
    if f.startswith(NEWS_PREFIXES):
        return "news"            # outlets republish each other
    if f.startswith(("firms_", "viirs", "modis")):
        return "polar_viirs_modis"
    if "slstr" in f:
        return "slstr"
    return "unknown:" + f        # an unrecognised source counts as its own instrument (fail-open)


def km(a, b, c, d):
    return math.hypot((a - c) * 111.32, (b - d) * 111.32 * math.cos(math.radians(a)))


def ts(s):
    return datetime.fromisoformat(str(s).replace("Z", "+00:00")).replace(tzinfo=None)


def main(path):
    con = sqlite3.connect(f"file:{path}?mode=ro", uri=True)
    events = con.execute("SELECT event_id, ts_last_utc, lat_center, lon_center, voter_list_json FROM voted").fetchall()
    firms = [(la, lo, ts(t)) for la, lo, t in con.execute("SELECT lat, lng, ts FROM firms") if la is not None]
    out = {"kept": [0, 0], "dropped": [0, 0], "combos": {}}
    for _eid, tl, la, lo, vl in events:
        fams = json.loads(vl or "[]")
        keep = len({instrument_of(f) for f in fams}) >= 2
        t = ts(tl)
        hit = any(abs(p[2] - t) <= timedelta(hours=12) and km(la, lo, p[0], p[1]) <= 5 for p in firms)
        k = "kept" if keep else "dropped"
        out[k][0] += 1
        out[k][1] += hit
        key = "+".join(sorted(fams)) + " -> " + k
        out["combos"][key] = out["combos"].get(key, 0) + 1
    print(json.dumps(out, indent=2, sort_keys=True))


if __name__ == "__main__":
    main(sys.argv[1])
