#!/usr/bin/env python3
"""Scores a run of scripts/run-maps-email-chain.py. No provider call.

Usage: scripts/score-maps-email-chain.py <run-dir> [<ledger.json>]
  <ledger.json> is optional: a list of `treg balance --json` entries for the run window,
  used for the settled cost per endpoint (reserve + release + settle summed per call).
Writes <run-dir>/summary.json and <run-dir>/places.csv (business names, domains and
counts only: no addresses, no personal names).
"""
import csv, json, sys
from collections import Counter, defaultdict
from pathlib import Path

RUN = Path(sys.argv[1])
chain = json.loads((RUN / "chain.json").read_text())
calls = [json.loads(l) for l in (RUN / "_calls.jsonl").read_text().splitlines() if l.strip()]
places = chain["places"]

def step(label): return label.split("-")[0]

def deliverable(e): return (e.get("status") or "").lower() in ("valid", "ok", "deliverable")
def catch_all(e): return (e.get("status") or "").lower() in ("catch_all", "catch-all", "accept_all", "risky") or e.get("catch_all") is True

def funnel(ps):
    f = Counter()
    for p in ps:
        f["places"] += 1
        if not p.get("website"): continue
        f["with_website"] += 1
        own = [e for e in p.get("emails", []) if e["on_domain"]]
        site = [e for e in own if "site" in e["sources"]]
        f["site_prints_email"] += bool(site)
        f["site_named_email"] += any(e["kind"] == "named" for e in site)
        f["any_email_on_domain"] += bool(own)
        f["generic_only"] += bool(own) and all(e["kind"] == "generic" for e in own)
        f["owner_named_by_search"] += bool(p.get("owner_found"))
        f["owner_email_found"] += bool(p.get("found_email"))
        named = [e for e in own if e["kind"] == "named"]
        f["named_email_any_source"] += bool(named)
        f["named_email_deliverable"] += any(deliverable(e) for e in named)
        f["any_deliverable"] += any(deliverable(e) for e in own)
        f["hunter_accept_all_domain"] += p.get("hunter_accept_all") is True
    return dict(f)

by_query = {q: funnel([p for p in places if p["query"] == q]) for q in chain["queries"]}
total = funnel(places)

addr = Counter(); status = Counter(); served = defaultdict(Counter)
for p in places:
    for e in p.get("emails", []):
        addr["addresses"] += 1; addr["on_domain" if e["on_domain"] else "off_domain"] += 1
        addr[e["kind"]] += 1; status[(e.get("status") or "none").lower()] += 1
        for s in e["sources"]: addr["source:" + s] += 1
for c in calls: served[step(c["label"])][c.get("served_by") or c["endpoint"]] += 1

cost = defaultdict(int); calls_n = Counter(step(c["label"]) for c in calls)
for c in calls: cost[step(c["label"])] += c.get("charged_micro") or 0
def ledger_step(ep):
    if ep.startswith("serper."): return "places"
    if ".web." in ep: return "site"
    if ep == "hunter.companies.emails": return "hunter"
    if ".verify" in ep: return "verify"
    if ".email.find" in ep or ep == "findymail.search.name": return "find"
    return "owner"  # people search, role and employee finders tried by the routed search

ledger_cost = step_cost = None
if len(sys.argv) > 2:
    # one call_id per provider attempt: a reserve, then a release (not billed) or a settle (billed settled_micro)
    by_call = defaultdict(list)
    for e in json.loads(Path(sys.argv[2]).read_text()): by_call[e["call_id"]].append(e)
    ledger_cost = defaultdict(int); step_cost = defaultdict(int)
    for es in by_call.values():
        kinds = {e["kind"] for e in es}
        if "settle" in kinds: c = sum(e["meta"].get("settled_micro", -e["amount_micro"]) for e in es if e["kind"] == "settle")
        elif "release" in kinds: c = 0
        else: c = -sum(e["amount_micro"] for e in es if e["kind"] == "reserve")
        ledger_cost[es[0]["endpoint_id"]] += c; step_cost[ledger_step(es[0]["endpoint_id"])] += c
    ledger_cost = {k: v for k, v in ledger_cost.items() if v}; step_cost = dict(step_cost)

# what each path through the chain yields on its own: verified (status valid) named address on the place's domain
paths = {}
if step_cost:
    vcost = {}
    for p in places:
        for i, e in enumerate(p.get("emails", [])):
            f = RUN / "raw" / f"verify-{p['id']}-{i}.json"
            vcost[(p["id"], i)] = (json.loads(f.read_text()).get("_treg") or {}).get("charged_micro") or 0 if f.exists() else 0
    H = lambda e: any(s.startswith("hunter") for s in e["sources"]); F = lambda e: "find" in e["sources"]; S = lambda e: "site" in e["sources"]
    for name, steps, f in [("site_only", ["places", "site"], S), ("hunter_domain_search", ["places", "hunter"], H),
                           ("owner_search_then_find", ["places", "owner", "find"], F), ("all_steps", ["places", "site", "hunter", "owner", "find"], lambda e: True)]:
        n = sum(any(e["on_domain"] and e["kind"] == "named" and deliverable(e) and f(e) for e in p.get("emails", [])) for p in places)
        c = sum(step_cost.get(s, 0) for s in steps) + sum(vcost[(p["id"], i)] for p in places for i, e in enumerate(p.get("emails", [])) if f(e))
        paths[name] = {"places_with_verified_named_email": n, "usd": round(c / 1e6, 4), "usd_per_verified_named": round(c / 1e6 / n, 4) if n else None}

lat = defaultdict(list)
for c in calls: lat[step(c["label"])].append(c["latency_ms"])
med = {k: sorted(v)[len(v) // 2] for k, v in lat.items()}

summary = {"queries": chain["queries"], "funnel_total": total, "funnel_by_query": by_query,
           "addresses": dict(addr), "verify_status": dict(status),
           "calls_by_step": dict(calls_n), "charged_micro_by_step_from_responses": dict(cost),
           "settled_micro_by_endpoint_from_ledger": ledger_cost, "settled_micro_by_step_from_ledger": step_cost, "paths": paths,
           "median_latency_ms_by_step": med, "served_by_by_step": {k: dict(v) for k, v in served.items()}}
(RUN / "summary.json").write_text(json.dumps(summary, indent=1))

with open(RUN / "places.csv", "w", newline="") as f:
    w = csv.writer(f)
    w.writerow(["id", "query", "business", "domain", "site_emails_generic", "site_emails_named", "hunter_emails", "hunter_accept_all",
                "owner_named", "owner_email_found", "named_deliverable", "any_deliverable"])
    for p in places:
        em = [e for e in p.get("emails", []) if e["on_domain"]]
        w.writerow([p["id"], p["query"], p.get("name"), p.get("domain"),
                    sum(1 for e in em if "site" in e["sources"] and e["kind"] == "generic"),
                    sum(1 for e in em if "site" in e["sources"] and e["kind"] == "named"),
                    sum(1 for e in em if any(s.startswith("hunter") for s in e["sources"])),
                    p.get("hunter_accept_all"), p.get("owner_found"), bool(p.get("found_email")),
                    any(deliverable(e) for e in em if e["kind"] == "named"), any(deliverable(e) for e in em)])
print(json.dumps(summary, indent=1))
