#!/usr/bin/env python3
"""Perbaikan harian tslaw.id - 16 Oktober 2026.
PRIORITAS 2: artikel #2931 (6-cara-mendapatkan-bantuan-hukum-gratis-di-surabaya-...) 750 kata -> 1.000+ kata
dengan isi berguna: dasar hukum (UU 16/2011, UU 48/2009, UU 18/2003, KUHAP), beda litigasi/nonlitigasi,
cara memastikan lembaga resmi (akreditasi BPHN), larangan meminta bayaran (Pasal 20), Posbakum+prodeo,
3 FAQ baru + skema FAQPage diselaraskan 5 -> 8 item, dan perbaikan integritas HTML (<p><p>).

Tulis lewat REST API JSON (BUKAN XML-RPC). Dry run default, --apply untuk menulis.
"""
import base64, json, re, sys, time, urllib.request, urllib.error

DOM = "tslaw.id"
USER, PASS = "bambangbot", "EFKm EYGx VQCP Y9go ncPr uM2Y"
AUTH = base64.b64encode(f"{USER}:{PASS}".encode()).decode()
UA = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124 Safari/537.36"}
FOLDER = "/home/mantapman/.hermes/profiles/atun/workspace/tslaw_pemulihan/perbaikan_20261016"
POST_ID = 2931

BLOK_BARU = """<h2>Dasar Hukum Bantuan Hukum Gratis di Indonesia</h2>
<p>Bantuan hukum gratis di Indonesia bukan program kebaikan hati semata, melainkan kewajiban negara yang diatur undang-undang. Undang-Undang Nomor 16 Tahun 2011 tentang Bantuan Hukum mendefinisikan Bantuan Hukum sebagai jasa hukum yang diberikan Pemberi Bantuan Hukum secara cuma-cuma kepada Penerima Bantuan Hukum. Pasal 1 angka 2 undang-undang yang sama menyebut Penerima Bantuan Hukum adalah orang atau kelompok orang miskin.</p>
<p>Pasal 4 UU 16/2011 mengatur bahwa Bantuan Hukum diberikan kepada Penerima Bantuan Hukum yang menghadapi masalah hukum, meliputi masalah hukum keperdataan, pidana, dan tata usaha negara, baik secara litigasi maupun nonlitigasi. Ayat berikutnya merinci bentuk bantuannya: menjalankan kuasa, mendampingi, mewakili, membela, dan melakukan tindakan hukum lain untuk kepentingan hukum penerima bantuan.</p>
<p>Batasan siapa yang tergolong miskin dijelaskan Pasal 5. Ayat (1) menyebut setiap orang atau kelompok orang miskin yang tidak dapat memenuhi hak dasar secara layak dan mandiri. Ayat (2) merinci hak dasar itu meliputi hak atas pangan, sandang, layanan kesehatan, layanan pendidikan, pekerjaan dan berusaha, dan perumahan. Dari rumusan ini terlihat bahwa penilaiannya tidak berhenti pada nominal penghasilan, tetapi pada kemampuan memenuhi kebutuhan dasar.</p>
<p>Dasar hukum lain yang menguatkan hak ini: Pasal 56 dan Pasal 57 Undang-Undang Nomor 48 Tahun 2009 tentang Kekuasaan Kehakiman menyatakan setiap orang yang tersangkut perkara berhak memperoleh bantuan hukum dan negara menanggung biaya perkara bagi pencari keadilan yang tidak mampu, serta pada setiap pengadilan negeri dibentuk pos bantuan hukum bagi mereka. Bantuan hukum itu diberikan secara cuma-cuma pada semua tingkat peradilan sampai putusan terhadap perkara tersebut memperoleh kekuatan hukum tetap. Pasal 22 ayat (1) Undang-Undang Nomor 18 Tahun 2003 tentang Advokat juga mewajibkan advokat memberikan bantuan hukum secara cuma-cuma kepada pencari keadilan yang tidak mampu. Khusus perkara pidana, Pasal 56 ayat (1) KUHAP menetapkan bahwa bagi tersangka atau terdakwa yang diancam pidana mati atau pidana lima belas tahun atau lebih, dan bagi yang tidak mampu dengan ancaman pidana lima tahun atau lebih yang tidak mempunyai penasihat hukum sendiri, pejabat pada semua tingkat pemeriksaan wajib menunjuk penasihat hukum bagi mereka.</p>
<h2>Bantuan Hukum Litigasi dan Nonlitigasi, Apa Bedanya</h2>
<p>Pasal 4 ayat (2) UU 16/2011 membagi bantuan hukum menjadi dua jalur. Litigasi adalah pendampingan yang masuk ke proses peradilan, mulai dari penyusunan gugatan atau pembelaan, pendampingan pemeriksaan, sampai mewakili di persidangan. Nonlitigasi berjalan di luar pengadilan, misalnya konsultasi hukum, penyuluhan, pemeriksaan dokumen, dan pendampingan saat perundingan. Pasal 9 UU 16/2011 memberi pemberi bantuan hukum kewenangan menyelenggarakan penyuluhan hukum, konsultasi hukum, dan program kegiatan lain yang berkaitan dengan penyelenggaraan bantuan hukum.</p>
<p>Praktisnya, nonlitigasi cocok untuk masalah yang masih bisa diselesaikan lewat surat dan perundingan, seperti somasi, utang piutang, atau sengketa kontrak. Litigasi dibutuhkan ketika perkara sudah masuk pengadilan dan memerlukan surat kuasa khusus. Karena jalur litigasi menuntut sumber daya lebih besar, lembaga bantuan hukum biasanya memilih urutan: penanganan nonlitigasi lebih dulu, lalu mendampingi sampai ke pengadilan bila perundingan gagal.</p>
<h2>Cara Memastikan Lembaga Bantuan Hukum Resmi</h2>
<p>Tidak semua pihak yang mengaku pemberi bantuan hukum punya dasar untuk menjalankan program ini. Pasal 7 ayat (1) huruf b UU 16/2011 memberi kewenangan kepada Menteri untuk melakukan verifikasi dan akreditasi terhadap lembaga bantuan hukum atau organisasi kemasyarakatan. Pasal 8 mengatur syaratnya: pemberi bantuan hukum harus berbadan hukum, terakreditasi berdasarkan undang-undang tersebut, memiliki kantor atau sekretariat tetap, memiliki pengurus, dan memiliki program bantuan hukum.</p>
<p>Tata cara verifikasi dan akreditasi terbaru diatur Peraturan Menteri Hukum Nomor 37 Tahun 2025, yang berlaku sejak 23 Oktober 2025 dan mencabut aturan sebelumnya. Hasil verifikasi diumumkan terbuka. Badan Pembinaan Hukum Nasional menerbitkan pengumuman lembaga atau organisasi bantuan hukum yang lulus verifikasi dan akreditasi sebagai pemberi bantuan hukum untuk periode 2025 sampai 2027 di laman resminya.</p>
<p>Sebelum melangkah, periksa dulu apakah lembaga tujuan Anda ada dalam daftar tersebut. Portal informasi bantuan hukum BPHN menyediakan menu daftar organisasi bantuan hukum terdekat dan pos bantuan hukum terdekat, termasuk Sistem Informasi Database Bantuan Hukum untuk pengajuan permohonan. Nama yang tidak pernah muncul di daftar resmi sebaiknya tidak dijadikan tujuan pertama.</p>
<h2>Larangan Meminta Bayaran yang Perlu Diketahui</h2>
<p>Ada satu pasal yang layak diingat siapa pun yang sedang mencari bantuan hukum gratis. Pasal 20 UU 16/2011 menyatakan Pemberi Bantuan Hukum dilarang menerima atau meminta pembayaran dari Penerima Bantuan Hukum dan/atau pihak lain yang terkait dengan perkara yang sedang ditangani pemberi bantuan hukum tersebut.</p>
<p>Karena itu, permintaan uang dalam bentuk apa pun, termasuk yang disebut biaya administrasi, biaya transportasi, atau biaya penyelesaian perkara, patut dicurigai bila datang dari pihak yang mengaku menyalurkan bantuan hukum gratis. Langkah aman: hubungi langsung kantor lembaga bantuan hukum atau Posbakum di pengadilan, mintakan nomor tanda terima resmi untuk setiap dokumen yang diserahkan, dan jangan menitipkan dokumen asli kepada perantara yang tidak bisa menunjukkan identitas lembaganya.</p>
<h2>Layanan Cuma-Cuma di Pengadilan: Posbakum dan Pembebasan Biaya Perkara</h2>
<p>Di lingkungan pengadilan, layanan bagi masyarakat tidak mampu diatur Peraturan Mahkamah Agung Nomor 1 Tahun 2014 tentang Pedoman Pemberian Layanan Hukum bagi Masyarakat Tidak Mampu di Pengadilan. Layanan itu meliputi pembebasan biaya perkara, persidangan di luar gedung pengadilan, dan Pos Bantuan Hukum atau Posbakum.</p>
<p>Posbakum memberi informasi, konsultasi, dan bantuan penyusunan dokumen yang dibutuhkan pencari keadilan, termasuk bagi yang belum mampu menyusun permohonan sendiri. Sifatnya cuma-cuma, sejalan dengan Pasal 57 ayat (2) UU 48/2009 yang menyatakan bantuan hukum diberikan secara cuma-cuma pada semua tingkat peradilan sampai putusan memperoleh kekuatan hukum tetap. Untuk memanfaatkannya, siapkan identitas diri, kronologi perkara secara berurutan, dan dokumen yang berkaitan dengan perkara tersebut.</p>"""

FAQ_TAMBAHAN = [
    ("Apa dasar hukum bantuan hukum gratis di Indonesia?",
     "Undang-Undang Nomor 16 Tahun 2011 tentang Bantuan Hukum, Pasal 56 dan Pasal 57 Undang-Undang Nomor 48 Tahun 2009 tentang Kekuasaan Kehakiman, Pasal 22 Undang-Undang Nomor 18 Tahun 2003 tentang Advokat, serta Pasal 56 KUHAP untuk perkara pidana."),
    ("Bolehkah lembaga bantuan hukum meminta bayaran kepada penerima bantuan hukum?",
     "Tidak. Pasal 20 Undang-Undang Nomor 16 Tahun 2011 melarang pemberi bantuan hukum menerima atau meminta pembayaran dari penerima bantuan hukum maupun dari pihak lain yang terkait dengan perkara yang sedang ditangani."),
    ("Bagaimana cara memeriksa apakah sebuah lembaga bantuan hukum resmi?",
     "Periksa apakah lembaga itu sudah terakreditasi menurut Pasal 7 dan Pasal 8 Undang-Undang Nomor 16 Tahun 2011. Hasil verifikasi dan akreditasi diumumkan BPHN, dan daftar organisasi bantuan hukum maupun Posbakum terdekat tersedia di portal informasi bantuan hukum BPHN."),
]

ISI = BLOK_BARU  # dibaca cek_pola_ai.py


def rest(path, data=None, method=None, timeout=180):
    h = {"Authorization": f"Basic {AUTH}", **UA}
    body = None
    if data is not None:
        body = json.dumps(data).encode()
        h["Content-Type"] = "application/json"
    r = urllib.request.Request(f"https://{DOM}{path}", data=body, headers=h, method=method)
    for i in range(5):
        try:
            with urllib.request.urlopen(r, timeout=timeout) as x:
                return x.status, json.loads(x.read())
        except urllib.error.HTTPError as e:
            return e.code, e.read().decode()[:500]
        except Exception as e:
            print(f"  (ulang {i+1}: {str(e)[:70]})")
            time.sleep(10)
    return 0, "gagal"


def kata(html, dengan_skema=True):
    t = html
    if not dengan_skema:
        t = re.sub(r"<script[^>]*>.*?</script>", " ", t, flags=re.S | re.I)
    t = re.sub(r"<[^>]+>", " ", t)
    t = t.replace("&nbsp;", " ").replace("&amp;", "&")
    return len(re.sub(r"\s+", " ", t).split())


def norm(s):
    return re.sub(r"\s+", " ", re.sub(r"<[^>]+>", " ", s)).strip()


def bangun(lama):
    info = {}
    anchor_faq = "<h2>Pertanyaan yang Sering Diajukan</h2>"
    assert lama.count(anchor_faq) == 1, f"anchor FAQ tidak unik: {lama.count(anchor_faq)}"

    baru = lama.replace(anchor_faq, BLOK_BARU + "\n\n" + anchor_faq, 1)

    m = re.search(r'<script type="application/ld\+json">(\{.*?\})</script>', baru, re.S)
    assert m and baru.count('<script type="application/ld+json">') == 1, "blok skema FAQPage tidak unik/absen"
    skema = json.loads(m.group(1))
    assert skema.get("@type") == "FAQPage", "skema lama bukan FAQPage"
    tanya_lama = [q["name"] for q in skema["mainEntity"]]
    info["faq_lama"] = tanya_lama
    for q, a in FAQ_TAMBAHAN:
        assert q not in tanya_lama, f"pertanyaan sudah ada: {q}"

    blok_faq = "\n".join([f"<h3>{q}</h3>\n<p>{a}</p>" for q, a in FAQ_TAMBAHAN])
    baru = baru.replace('<script type="application/ld+json">', blok_faq + "\n\n" + '<script type="application/ld+json">', 1)

    skema["mainEntity"] = skema["mainEntity"] + [
        {"@type": "Question", "name": q, "acceptedAnswer": {"@type": "Answer", "text": a}} for q, a in FAQ_TAMBAHAN]
    skrip_baru = '<script type="application/ld+json">' + json.dumps(skema, ensure_ascii=False) + "</script>"
    m_skrip = re.search(r'<script type="application/ld\+json">\{.*?\}</script>', baru, re.S)
    assert m_skrip, "blok skema tidak ditemukan setelah penyisipan FAQ"
    baru = baru[:m_skrip.start()] + skrip_baru + baru[m_skrip.end():]

    # integritas: paragraf bersarang <p><p>...</p></p>
    info["p_nested"] = baru.count("</p></p>")
    baru = baru.replace("</p></p>", "</p>")

    # skema harus benar-benar tampil di isi
    m2 = re.search(r'<script type="application/ld\+json">(\{.*?\})</script>', baru, re.S)
    skema2 = json.loads(m2.group(1))
    assert len(skema2["mainEntity"]) == len(tanya_lama) + len(FAQ_TAMBAHAN), "jumlah item skema tidak cocok"
    isi_tanpa_skrip = re.sub(r"<script[^>]*>.*?</script>", " ", baru, flags=re.S | re.I)
    for q in skema2["mainEntity"]:
        assert f"<h3>{q['name']}</h3>" in isi_tanpa_skrip, f"pertanyaan skema tidak tampil: {q['name']}"
        assert norm(q["acceptedAnswer"]["text"]) in norm(isi_tanpa_skrip), f"jawaban skema tidak ada di isi: {q['name']}"
    info["faq_baru"] = len(skema2["mainEntity"])

    # kalimat lama utuh (normalisasi spasi + buang tag dulu, baru pisah per kalimat)
    kal = [k for k in re.split(r"(?<=[.?!])\s+", norm(re.sub(r"<script[^>]*>.*?</script>", " ", lama, flags=re.S | re.I)))
           if len(k.split()) > 4]
    teks_baru = norm(re.sub(r"<script[^>]*>.*?</script>", " ", baru, flags=re.S | re.I))
    hilang = [k[:90] for k in kal if k not in teks_baru]
    info["kalimat_lama"] = len(kal)
    info["hilang"] = hilang
    assert not hilang, f"kalimat lama hilang: {hilang[:3]}"

    # pemeriksaan gaya pada naskah baru
    teks_norm = re.sub(r"<[^>]+>", " ",
                       BLOK_BARU + " " + " ".join(q + " " + a for q, a in FAQ_TAMBAHAN))
    for nama, pola in {"em dash": "\u2014", "kutip melengkung": "[\u201c\u201d\u2018\u2019]", "CJK": "[\u3000-\u9fff]",
                       "tanda seru": "!", "strong": "<strong>"}.items():
        assert not re.search(pola, teks_norm), f"pola dilarang ({nama})"
    for frasa in ["di era digital", "tidak hanya", "tak dapat dipungkiri", "perlu dicatat bahwa",
                  "sangat penting untuk", "dalam dunia yang"]:
        assert frasa not in teks_norm.lower(), f"frasa AI: {frasa}"
    return baru, info


def main():
    terapkan = "--apply" in sys.argv
    st, p = rest(f"/wp-json/wp/v2/posts/{POST_ID}?context=edit&_fields=id,slug,link,title,content,meta")
    assert st == 200, f"gagal ambil post: {st} {p}"
    lama = p["content"]["raw"]
    judul_lama = p["title"]["raw"]
    fokus_lama = (p.get("meta") or {}).get("_yoast_wpseo_focuskw")
    meta_lama = (p.get("meta") or {}).get("_yoast_wpseo_metadesc")

    baru, info = bangun(lama)
    print(f"post #{p['id']}  {p['link']}")
    print(f"judul: {judul_lama}")
    print(f"kata sebelum: {kata(lama)} (tanpa skema {kata(lama, False)}) -> "
          f"sesudah: {kata(baru)} (tanpa skema {kata(baru, False)})")
    print(f"H2 {lama.count('<h2')} -> {baru.count('<h2')} | H3 {lama.count('<h3')} -> {baru.count('<h3')}")
    print(f"FAQ lama {len(info['faq_lama'])} -> {info['faq_baru']} | <p><p> rusak {info['p_nested']} -> {baru.count('</p></p>')}")
    print(f"kalimat lama diperiksa: {info['kalimat_lama']}, hilang: {len(info['hilang'])}")
    wa_jml = len(re.findall(r"wa\.me/\d{6,}", baru))
    print(f"tombol WA: {wa_jml} | skema FAQPage: {baru.count('FAQPage')}")

    if not terapkan:
        open(f"{FOLDER}/post_{POST_ID}_SESUDAH_dryrun.html", "w").write(baru)
        print("\nDRY RUN selesai. Jalankan dengan --apply untuk menulis.")
        return
    st, resp = rest(f"/wp-json/wp/v2/posts/{POST_ID}", {"content": baru}, method="POST")
    print(f"\nPOST REST -> HTTP {st}")
    json.dump(resp if isinstance(resp, dict) else {"raw": str(resp)},
              open(f"{FOLDER}/post_{POST_ID}_POSTRESP.json", "w"), ensure_ascii=False, indent=1)
    if st != 200:
        print("GAGAL:", str(resp)[:400])
        sys.exit(1)
    st2, p2 = rest(f"/wp-json/wp/v2/posts/{POST_ID}?context=edit&_fields=id,slug,link,title,content,meta")
    ulang = p2["content"]["raw"]
    verif = {
        "http_baca_ulang": st2,
        "isi_identik_dengan_yang_dikirim": ulang == baru,
        "kata_api": kata(ulang),
        "kata_api_tanpa_skema": kata(ulang, False),
        "judul_sama": (p2["title"]["raw"] == judul_lama),
        "slug_sama": (p2["slug"] == p["slug"]),
        "fokus_sama": ((p2.get("meta") or {}).get("_yoast_wpseo_focuskw") == fokus_lama),
        "metadesc_sama": ((p2.get("meta") or {}).get("_yoast_wpseo_metadesc") == meta_lama),
        "faq_tampil": len(re.findall(r"<h3>[^<]*\?</h3>", ulang)),
        "item_skema": len(json.loads(re.search(r'<script type="application/ld\+json">(\{.*?\})</script>',
                                               ulang, re.S).group(1))["mainEntity"]),
        "tombol_wa": len(re.findall(r"wa\.me/\d{6,}", ulang)),
    }
    json.dump(verif, open(f"{FOLDER}/post_{POST_ID}_VERIFIKASI.json", "w"), ensure_ascii=False, indent=1)
    print(json.dumps(verif, ensure_ascii=False, indent=1))
    open(f"{FOLDER}/post_{POST_ID}_SESUDAH.html", "w").write(ulang)
    if not verif["isi_identik_dengan_yang_dikirim"]:
        print("PERINGATAN: isi hasil baca ulang tidak identik!")
        sys.exit(1)


if __name__ == "__main__":
    import os
    if not os.path.isdir(FOLDER):
        print(f"folder {FOLDER} belum ada, buat dulu.")
        sys.exit(1)
    if "--apply" in sys.argv and not os.path.exists(f"{FOLDER}/post_{POST_ID}_SEBELUM.html"):
        st, p = rest(f"/wp-json/wp/v2/posts/{POST_ID}?context=edit&_fields=id,slug,link,title,content,meta")
        json.dump(p, open(f"{FOLDER}/post_{POST_ID}_SEBELUM.json", "w"), ensure_ascii=False)
        open(f"{FOLDER}/post_{POST_ID}_SEBELUM.html", "w").write(p["content"]["raw"])
        print("salinan SEBELUM disimpan.")
    main()
