#!/usr/bin/env python3
"""Bangun dasbor KPI untuk SEMUA klien: satu berkas HTML + satu PNG per klien.
Pakai: python3 kpi_dashboard_klien.py [slug ...]     (tanpa argumen = semua klien)
"""
import html, json, os, sys, time, urllib.parse, urllib.request, urllib.error
from datetime import date, datetime, timedelta

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
sys.path.insert(0, WS)
from kpi_konfigurasi_klien import KLIEN, TANPA_AKSES          # noqa: E402

TOKEN = f"{WS}/seo-oauth/google_token_atun.json"
KELUAR = f"{WS}/drafts/kpi"
os.makedirs(KELUAR, exist_ok=True)

try:
    from google.oauth2.credentials import Credentials
    from google.auth.transport.requests import Request
    creds = Credentials.from_authorized_user_file(TOKEN)
    if creds.expired and creds.refresh_token:
        creds.refresh(Request()); open(TOKEN, "w").write(creds.to_json())
except Exception as e:
    print("❌ Token Google tidak bisa dipakai:", str(e)[:150]); sys.exit(0)

H = {"Authorization": f"Bearer {creds.token}", "Content-Type": "application/json"}


def api(u, data=None):
    r = urllib.request.Request(u, headers=H, data=json.dumps(data).encode() if data else None)
    for _ in range(3):
        try:
            with urllib.request.urlopen(r, timeout=180) as x:
                return json.loads(x.read())
        except urllib.error.HTTPError as e:
            return {"err": e.code}
        except Exception:
            time.sleep(3)
    return {}


akhir = date.today() - timedelta(days=3)
awal = akhir - timedelta(days=27)
lalu_akhir = awal - timedelta(days=1)
lalu_awal = lalu_akhir - timedelta(days=27)
S = lambda x: x.isoformat()


def gsc(site_enc, dim, a, b, baris=500):
    d = api(f"https://www.googleapis.com/webmasters/v3/sites/{site_enc}/searchAnalytics/query",
            {"startDate": a, "endDate": b, "dimensions": dim, "rowLimit": baris})
    return (d or {}).get("rows", [])


def ga(prop, dim, met, hari=90, limit=20, urut="sessions"):
    d = api(f"https://analyticsdata.googleapis.com/v1beta/properties/{prop}:runReport",
            {"dateRanges": [{"startDate": f"{hari}daysAgo", "endDate": "today"}],
             "dimensions": [{"name": x} for x in dim],
             "metrics": [{"name": x} for x in met], "limit": limit,
             "orderBys": [{"metric": {"metricName": urut}, "desc": True}]})
    return (d or {}).get("rows", [])


def delta(now, before):
    if not before:
        return "—", 0.0
    return f"{(now - before) / before * 100:+.0f}%", (now - before) / before * 100


CSS = """<style>
 body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:#f6f7f9;color:#202124;margin:0;padding:22px}
 h1{font-size:20px;margin:0 0 3px} .sub{color:#5f6368;font-size:12px;margin-bottom:18px}
 .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-bottom:20px}
 .kartu{background:#fff;border:1px solid #e3e6ea;border-radius:9px;padding:12px 14px}
 .label{font-size:11px;color:#5f6368} .nilai{font-size:25px;font-weight:700;margin:5px 0 2px}
 .catatan{font-size:11px}
 h2{font-size:14px;margin:20px 0 7px}
 table{width:100%;border-collapse:collapse;background:#fff;border:1px solid #e3e6ea;border-radius:9px;overflow:hidden}
 th,td{padding:8px 11px;font-size:12.5px;text-align:left;border-bottom:1px solid #eef0f2}
 th{background:#fafbfc;color:#5f6368;font-weight:600} .angka{text-align:right}
 .bar{background:#eef0f2;border-radius:4px;height:9px} .bar span{display:block;height:9px;background:#1a73e8;border-radius:4px}
 footer{color:#5f6368;font-size:11.5px;margin-top:20px}
</style>"""


def bangun(k):
    site_enc = urllib.parse.quote(k["gsc"], safe="")
    ini_rows = gsc(site_enc, ["date"], S(awal), S(akhir), 100)
    lalu_rows = gsc(site_enc, ["date"], S(lalu_awal), S(lalu_akhir), 100)
    tot = lambda rows: {"klik": sum(r["clicks"] for r in rows), "tayang": sum(r["impressions"] for r in rows),
                        "ctr": round(sum(r["clicks"] for r in rows) / sum(r["impressions"] for r in rows) * 100, 2)
                        if sum(r["impressions"] for r in rows) else 0,
                        "posisi": round(sum(r["position"] for r in rows) / len(rows), 1) if rows else 0}
    ini, lalu = tot(ini_rows), tot(lalu_rows)

    rows90 = gsc(site_enc, ["query"], S(akhir - timedelta(days=89)), S(akhir), 3000)
    pita = []
    for lo, hi in ((1, 3), (4, 10), (11, 20), (21, 50), (51, 100)):
        kl = sum(r["clicks"] for r in rows90 if lo <= r["position"] <= hi)
        ty = sum(r["impressions"] for r in rows90 if lo <= r["position"] <= hi)
        pita.append({"pita": f"{lo}-{hi}", "klik": kl, "tayang": ty, "ctr": round(kl / ty * 100, 2) if ty else 0})

    daftar_kata = k.get("kata") or [r["keys"][0] for r in sorted(rows90, key=lambda z: -z["impressions"])[:5]]
    posisi_kata = []
    for kata in daftar_kata[:5]:
        x = next((r for r in rows90 if r["keys"][0].lower() == kata.lower()), None)
        posisi_kata.append({"kata": kata, "posisi": round(x["position"], 1) if x else None,
                            "tayang": x["impressions"] if x else 0, "klik": x["clicks"] if x else 0})

    kueri = [{"k": r["keys"][0], "klik": r["clicks"], "tayang": r["impressions"], "pos": round(r["position"], 1)}
             for r in sorted(rows90, key=lambda z: -z["impressions"])[:10]]
    hal = [{"u": r["keys"][0].replace("https://", "").replace("http://", "") or "/", "klik": r["clicks"],
            "tayang": r["impressions"], "pos": round(r["position"], 1)}
           for r in sorted(gsc(site_enc, ["page"], S(awal), S(akhir), 200), key=lambda z: -z["clicks"])[:8]]

    kanal, peristiwa, sesi_total, sesi_org, sesi_ai = [], [], 0, 0, 0
    if k.get("ga4"):
        kanal = [{"k": r["dimensionValues"][0]["value"], "sesi": int(r["metricValues"][0]["value"]),
                  "keterlibatan": round(float(r["metricValues"][1]["value"]) * 100),
                  "kunci": r["metricValues"][2]["value"]}
                 for r in ga(k["ga4"], ["sessionDefaultChannelGroup"], ["sessions", "engagementRate", "keyEvents"])]
        peristiwa = [{"e": r["dimensionValues"][0]["value"], "n": r["metricValues"][0]["value"]}
                     for r in ga(k["ga4"], ["eventName"], ["eventCount"], 90, 25, "eventCount")]
        sesi_total = sum(x["sesi"] for x in kanal)
        sesi_org = next((x["sesi"] for x in kanal if x["k"] == "Organic Search"), 0)
        sesi_ai = next((x["sesi"] for x in kanal if x["k"] == "AI Assistant"), 0)

    kunci_wa = next((p["n"] for p in peristiwa if p["e"] in ("klik_wa", "wa_click", "click_whatsapp",
                                                             "contact_link_click")), "0")

    kartu_def = [("Klik organik (28 hari)", ini["klik"], lalu["klik"], "GSC", "naik"),
                 ("Tayangan (28 hari)", ini["tayang"], lalu["tayang"], "GSC", "naik"),
                 ("CTR rata-rata", ini["ctr"], lalu["ctr"], "GSC", "naik"),
                 ("Posisi rata-rata", ini["posisi"], lalu["posisi"], "GSC", "turun"),
                 ("Sesi organik (90 hari)", sesi_org, 0, "GA4", "naik"),
                 ("Sesi kanal AI (90 hari)", sesi_ai, 0, "GA4", "naik")]
    kartu = ""
    for nama, nilai, sebelum, alat, arah in kartu_def:
        if sebelum:
            td, p = delta(float(nilai), float(sebelum))
            baik = (p < -1) if arah == "turun" else (p > 1)
            buruk = (p > 1) if arah == "turun" else (p < -1)
            w = "#0f9d58" if baik else ("#d93025" if buruk else "#5f6368")
            ket = "membaik" if baik else ("memburuk" if buruk else "datar")
            cat = f'<span style="color:{w}">{td} {ket}</span>'
        else:
            cat = '<span style="color:#5f6368">tanpa pembanding</span>'
        kartu += (f'<div class="kartu"><div class="label">{nama}</div><div class="nilai">{nilai}</div>'
                  f'<div class="catatan">{cat} · {alat}</div></div>')

    maks = max((p["tayang"] for p in pita), default=1)
    baris_pita = "".join(
        f'<tr><td>posisi {p["pita"]}</td><td><div class="bar"><span style="width:{round(p["tayang"]/maks*100) if maks else 0}%"></span></div></td>'
        f'<td class="angka">{p["tayang"]:,}</td><td class="angka">{p["klik"]}</td><td class="angka">{p["ctr"]}%</td></tr>'
        for p in pita)
    baris_kata = "".join(
        f'<tr><td>{html.escape(p["kata"])}</td>'
        f'<td class="angka" style="font-weight:600;color:{"#0f9d58" if (p["posisi"] or 99)<=10 else ("#f9ab00" if (p["posisi"] or 99)<=30 else "#d93025")}">'
        f'{p["posisi"] if p["posisi"] is not None else "—"}</td>'
        f'<td class="angka">{p["tayang"]:,}</td><td class="angka">{p["klik"]}</td></tr>' for p in posisi_kata)
    baris_kanal = "".join(f'<tr><td>{x["k"]}</td><td class="angka">{x["sesi"]:,}</td>'
                          f'<td class="angka">{x["keterlibatan"]}%</td><td class="angka">{x["kunci"]}</td></tr>' for x in kanal)
    baris_kueri = "".join(f'<tr><td>{html.escape(x["k"][:56])}</td><td class="angka">{x["tayang"]:,}</td>'
                          f'<td class="angka">{x["klik"]}</td><td class="angka">{x["pos"]}</td></tr>' for x in kueri)
    baris_hal = "".join(f'<tr><td>{html.escape(x["u"][:60])}</td><td class="angka">{x["klik"]}</td>'
                        f'<td class="angka">{x["tayang"]:,}</td><td class="angka">{x["pos"]}</td></tr>' for x in hal)

    waktu = datetime.now().strftime("%d %B %Y, %H:%M WIB")
    htm = f"""<!DOCTYPE html><html lang="id"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><title>Dasbor KPI {k['nama']}</title>{CSS}</head><body>
<h1>Dasbor KPI {html.escape(k['nama'])}</h1>
<div class="sub">GSC {html.escape(k['gsc'])} · GA4 {k.get('ga4') or '-'} · dibuat {waktu} · data GSC sampai {S(akhir)}</div>
<div class="grid">{kartu}</div>
<h2>Sebaran posisi (90 hari)</h2>
<table><thead><tr><th>Pita posisi</th><th>Tayangan</th><th class="angka">Tayangan</th><th class="angka">Klik</th><th class="angka">CTR</th></tr></thead><tbody>{baris_pita}</tbody></table>
<h2>Kata kunci uang</h2>
<table><thead><tr><th>Kata kunci</th><th class="angka">Posisi</th><th class="angka">Tayangan</th><th class="angka">Klik</th></tr></thead><tbody>{baris_kata}</tbody></table>
<h2>Kanal kedatangan (GA4, 90 hari) — total {sesi_total:,} sesi</h2>
<table><thead><tr><th>Kanal</th><th class="angka">Sesi</th><th class="angka">Keterlibatan</th><th class="angka">Peristiwa kunci</th></tr></thead><tbody>{baris_kanal}</tbody></table>
<h2>Kueri tayangan terbanyak (90 hari)</h2>
<table><thead><tr><th>Kueri</th><th class="angka">Tayangan</th><th class="angka">Klik</th><th class="angka">Posisi</th></tr></thead><tbody>{baris_kueri}</tbody></table>
<h2>Halaman teratas (28 hari)</h2>
<table><thead><tr><th>Halaman</th><th class="angka">Klik</th><th class="angka">Tayangan</th><th class="angka">Posisi</th></tr></thead><tbody>{baris_hal}</tbody></table>
<footer>Peristiwa klik kontak/WhatsApp (90 hari): <strong>{kunci_wa}</strong>. Dibuat otomatis oleh Atun.</footer>
</body></html>"""
    keluar = f"{KELUAR}/kpi_{k['slug']}.html"
    open(keluar, "w").write(htm)
    ringkas = {"nama": k["nama"], "klik": ini["klik"], "d_klik": delta(ini["klik"], lalu["klik"])[0],
               "tayang": ini["tayang"], "ctr": ini["ctr"], "posisi": ini["posisi"],
               "sesi_total": sesi_total, "organik": sesi_org, "ai": sesi_ai, "kontak": kunci_wa,
               "berkas": keluar}
    print(f"   {k['nama']:<26} klik {ini['klik']:>4} ({ringkas['d_klik']:>5}) | tayang {ini['tayang']:>6} | "
          f"CTR {ini['ctr']:>5}% | pos {ini['posisi']:>5} | sesi {sesi_total:>5} | kontak {kunci_wa}")
    return ringkas


pilih = sys.argv[1:]
hasil = []
print(f"=== membangun dasbor {date.today()} ===")
for k in KLIEN:
    if pilih and k["slug"] not in pilih:
        continue
    try:
        hasil.append(bangun(k))
    except Exception as e:
        print(f"   ❌ {k['nama']}: {str(e)[:90]}")
    time.sleep(0.5)

json.dump(hasil, open(f"{KELUAR}/ringkasan_kpi.json", "w"), indent=1, ensure_ascii=False)
print(f"\n{len(hasil)} dasbor dibuat → {KELUAR}/")
if TANPA_AKSES:
    print("\nTanpa akses GSC (tidak bisa diukur, hanya GA4):")
    for nama, site, ga4 in TANPA_AKSES:
        print(f"   {nama} — {site} (GA4 {ga4})")
