"""Recompute the article figures from the public CSVs in this folder: python3 metrics.py"""
import csv, collections, itertools, re
M = list(csv.DictReader(open("hotel_mentions.csv", encoding="utf-8")))
S = list(csv.DictReader(open("sources.csv", encoding="utf-8")))
A = list(csv.DictReader(open("answers.csv", encoding="utf-8")))
P = lambda x: round(100 * x)
runs = collections.defaultdict(lambda: collections.defaultdict(set))  # (assistant, city, qtype) -> run -> hotel_ids
for m in M: runs[(m["assistant"], m["city"], m["question_type"])][m["run"]].add(m["hotel_id"])
J = lambda a, b: len(a & b) / len(a | b) if a | b else None
mean = lambda xs: sum(x for x in xs if x is not None) / len([x for x in xs if x is not None])
print("Rerun stability (share of one run's hotels named again in a second run | hotels in all 3 runs / hotels in any run)")
for e in ("ChatGPT", "Gemini", "DeepSeek"):
    back, all3 = [], []
    for (a, c, q), rr in runs.items():
        if a != e or len(rr) < 3: continue
        back += [len(x & y) / len(x) for x, y in itertools.permutations(rr.values(), 2) if x]
        u = set().union(*rr.values()); all3.append(len(set.intersection(*rr.values())) / len(u))
    print(f"  {e}: {P(mean(back))}% | {P(mean(all3))}%")
print("Cross-assistant overlap (hotels named by both / by either, all runs of a question)")
per = collections.defaultdict(lambda: collections.defaultdict(set))
for (a, c, q), rr in runs.items(): per[(c, q)][a] = set().union(*rr.values())
for x, y in itertools.combinations(("DeepSeek", "ChatGPT", "Gemini"), 2):
    print(f"  {x}-{y}: {P(mean([J(v[x], v[y]) for v in per.values() if v[x] and v[y]]))}%")
t = [len(v['DeepSeek'] & v['ChatGPT'] & v['Gemini']) / len(v['DeepSeek'] | v['ChatGPT'] | v['Gemini']) for v in per.values() if all(v[k] for k in ('DeepSeek', 'ChatGPT', 'Gemini'))]
print(f"  all three: {P(mean(t))}%")
print("Long tail per city (distinct hotels, % named once, top-5 share of mentions)")
for c in sorted({m["city"] for m in M}):
    cnt = collections.Counter(m["hotel_id"] for m in M if m["city"] == c)
    print(f"  {c}: {len(cnt)}, {P(sum(1 for v in cnt.values() if v == 1) / len(cnt))}%, {P(sum(v for _, v in cnt.most_common(5)) / sum(cnt.values()))}%")
print("Independent share of mentions (chain + independent only)")
def ind(rows):
    c = collections.Counter(r["chain_or_independent"] for r in rows); return P(c["independent"] / (c["independent"] + c["chain"]))
print("  all:", ind(M), {e: ind([m for m in M if m["assistant"] == e]) for e in ("ChatGPT", "Gemini", "DeepSeek")})
print("  by question:", {q: ind([m for m in M if m["question_type"] == q]) for q in ("boutique", "budget", "couples", "best", "family", "business", "luxury")})
print("Local-language vs English 'best' (same assistant, mean over run pairs) | English 'best' vs itself")
for c in ("Paris", "Tokyo", "Bangkok", "Bali", "Jakarta"):
    lv, ee = [], []
    for e in ("ChatGPT", "Gemini", "DeepSeek"):
        L, B = runs[(e, c, "local")], runs[(e, c, "best")]
        lv += [J(x, y) for x in L.values() for y in B.values()]
        ee += [J(x, y) for x, y in itertools.combinations(B.values(), 2)]
    print(f"  {c}: {P(mean(lv))}% | {P(mean(ee))}%")
print("Sources and links")
def share(rows, key):
    c = collections.Counter(r["domain_type"] for r in rows); t = sum(c.values()); return t, {k: P(v / t) for k, v in c.most_common()}
reg = lambda h: ".".join(h.split(".")[-3:]) if len(h.split(".")) >= 3 and h.split(".")[-2] in ("co", "com", "org", "net", "ac") and len(h.split(".")[-1]) == 2 else ".".join(h.split(".")[-2:])
for e in ("DeepSeek", "ChatGPT", "Gemini"):
    src = list({(r["answer_id"], reg(r["domain"])): r for r in S if r["assistant"] == e and r["role"] == "source"}.values())
    lk = [r for r in S if r["assistant"] == e and r["role"] == "link in answer"]
    print(f"  {e} sources (one per site per answer): {share(src, 'domain_type')}; answers with a source: {len({r['answer_id'] for r in src})}")
    if lk: print(f"  {e} links in answer: {share(lk, 'domain_type')}; answers with a link: {len({r['answer_id'] for r in lk})}")
print("  ChatGPT answers linking a hotel site:", sum(1 for a in A if a["assistant"] == "ChatGPT" and int(a["links_to_hotel_sites"]) > 0))
