"""Enumerate feedback loops in the near-term-to-catastrophic CLD (Figure 1).

Each edge is (cause, effect, polarity). A loop is reinforcing (R) when it
contains an even number of negative links and balancing (B) when odd.
"""
import networkx as nx

EDGES = [
    # governance and provenance capacity
    ("Governance capacity", "Mis/disinformation", "-"),
    ("Governance capacity", "Archive deepfake attacks", "-"),
    ("Provenance capacity", "Mis/disinformation", "-"),
    ("Provenance capacity", "Archive deepfake attacks", "-"),
    ("Governance capacity", "Provenance capacity", "+"),
    # Case 1: TVE pathway
    ("Mis/disinformation", "Radicalization", "+"),
    ("Radicalization", "Weapons development", "+"),
    ("Weapons development", "TVE attacks", "+"),
    ("Weapons development", "CBRN use", "+"),
    ("TVE attacks", "Mis/disinformation", "+"),
    ("TVE attacks", "Governance capacity", "+"),          # delayed response
    # Case 2 and 4: wartime disinformation and POW deepfakes
    ("Mis/disinformation", "Wartime disinformation", "+"),
    ("Wartime disinformation", "Interstate conflict", "+"),
    ("Interstate conflict", "POW deepfakes", "+"),
    ("POW deepfakes", "Wartime disinformation", "+"),
    ("Wartime disinformation", "TVE attacks", "+"),       # revenge terrorism
    ("Wartime disinformation", "Cyberattacks", "+"),
    ("Interstate conflict", "Nuclear escalation", "+"),
    ("Interstate conflict", "Institutional resilience", "-"),
    # Case 3: testimony archives and resilience
    ("Archive deepfake attacks", "Mis/disinformation", "+"),
    ("Archive deepfake attacks", "Institutional resilience", "-"),
    ("Mis/disinformation", "Institutional resilience", "-"),
    ("Institutional resilience", "Governance capacity", "+"),
    ("Institutional resilience", "TVE attacks", "-"),
    ("Institutional resilience", "Cyberattacks", "-"),
    ("Cyberattacks", "Institutional resilience", "-"),
]

CATASTROPHIC = {"CBRN use", "Nuclear escalation", "Cyberattacks"}


def build_graph(edges):
    g = nx.DiGraph()
    for cause, effect, sign in edges:
        g.add_edge(cause, effect, sign=sign)
    return g


def loop_polarity(g, cycle):
    links = zip(cycle, cycle[1:] + cycle[:1])
    negatives = sum(g[a][b]["sign"] == "-" for a, b in links)
    return "R" if negatives % 2 == 0 else "B"


def main():
    g = build_graph(EDGES)
    loops = sorted(nx.simple_cycles(g), key=len)
    counts = {"R": 0, "B": 0}
    for cycle in loops:
        counts[loop_polarity(g, cycle)] += 1
    print(f"{len(loops)} loops: {counts['R']} reinforcing, {counts['B']} balancing")

    # Leverage: how many feedback loops pass through each variable?
    on_loops = {n: sum(n in c for c in loops) for n in g}
    for node, k in sorted(on_loops.items(), key=lambda kv: -kv[1])[:5]:
        print(f"{node:26s} on {k:2d} of {len(loops)} loops")

    # Which stabilizing (balancing) loops depend on the delayed governance response?
    delayed = ("TVE attacks", "Governance capacity")
    balancing = [c for c in loops if loop_polarity(g, c) == "B"]
    via_delay = [c for c in balancing if delayed in zip(c, c[1:] + c[:1])]
    print(f"{len(via_delay)} of {len(balancing)} balancing loops pass through "
          f"the delayed TVE -> governance link")

    # Does every near-term variable have a directed path to a catastrophic outcome?
    near_term = [n for n in g if n not in CATASTROPHIC]
    reach_all = [n for n in near_term
                 if all(nx.has_path(g, n, c) for c in CATASTROPHIC)]
    print(f"{len(reach_all)} of {len(near_term)} near-term variables "
          f"reach all three catastrophic outcomes")


if __name__ == "__main__":
    main()
