"""Keep the copy of the test messages and their scores inside ../widget.html equal to what safety_filter.py computes.

    python3 embed_widget.py          rewrite the data block in widget.html
    python3 embed_widget.py --check  exit 1 if widget.html differs (test_widget.py runs the same check)
"""
import json
import re
import sys
from pathlib import Path

import safety_filter as sf

WIDGET = Path(__file__).resolve().parent.parent / "widget.html"
CONFIG_BLOCK = re.compile(r'(<script type="application/json" id="sfw-config">)(.*?)(</script>)', re.S)
# Drawing units of the plot: its width and height, the left edge and length of the score line, the middle of each row (harmful on top).
GEO = {"w": 300, "h": 214, "left": 10, "span": 280, "lanes": [64, 150], "half": 36, "gap": 6.0}


def swarm(scores, half, gap, span):
    """Vertical offsets for one row of marks, in score order. Each mark takes the place that keeps it farthest from the marks already
    placed (up to `gap`), nearest the middle of the row on a tie. Where many scores are almost equal the marks overlap; that is the pile."""
    spots = [0.0] + [sign * k * 0.5 for k in range(1, int(half / 0.5) + 1) for sign in (1, -1)]
    placed, offsets = [], []
    for s in scores:
        x = s * span
        near = [(px, py) for px, py in placed if abs(px - x) < gap]
        best = max(spots, key=lambda y: (min([((x - px) ** 2 + (y - py) ** 2) ** 0.5 for px, py in near] + [gap]), -abs(y)))
        placed.append((x, best))
        offsets.append(round(best, 1))
    return offsets


def messages():
    """The test messages in score order (ties by position), each with its row offset: {t: text, h: 1 if harmful, s: score, y: offset}."""
    r = sf.build()
    order = sorted(range(len(r["test"])), key=lambda i: (r["scores"][i], i))
    offsets = {}
    for label in (1, 0):
        row = [i for i in order if r["labels"][i] == label]
        for i, y in zip(row, swarm([r["scores"][i] for i in row], GEO["half"], GEO["gap"], GEO["span"])):
            offsets[i] = y
    out = [{"t": r["test"][i][0], "h": r["labels"][i], "s": r["scores"][i], "y": offsets[i]} for i in order]
    return out, [r["test"][i][0] for i in order]


def config():
    """What the page needs: the test messages with their scores, the two traffic mixes, the drawing units and the opening state."""
    msgs, texts = messages()
    opening = next(i for i, t in enumerate(texts) if t.endswith(sf.FALSE_ALARM_EXAMPLE))
    return {"messages": msgs, "traffic": {"total": sf.TRAFFIC, "harmful": sf.TRAFFIC_HARMFUL}, "geo": GEO,
            "opening": {"pct": sf.OPENING_PCT, "pick": opening}}


def embed(html):
    """html with the data block rewritten from safety_filter.py."""
    settings = json.dumps(config(), ensure_ascii=False, separators=(", ", ": "))
    return CONFIG_BLOCK.sub(lambda m: m.group(1) + "\n" + settings + "\n" + m.group(3), html, count=1)


if __name__ == "__main__":
    current = WIDGET.read_text(encoding="utf-8")
    updated = embed(current)
    if "--check" in sys.argv:
        sys.exit(0 if updated == current else 1)
    WIDGET.write_text(updated, encoding="utf-8")
    print("widget.html data block now matches safety_filter.py")
