#!/usr/bin/env python3
"""Perbaikan #3313 (klausul penyelesaian sengketa kontrak UMKM) - 17 Okt 2026.
Prioritas 2: panjangkan isi 674 -> 1.000+ kata dengan dasar hukum yang dicek hari ini.

Pakai:
  python3 tslaw_fix_3313_20261017.py            (dry run)
  python3 tslaw_fix_3313_20261017.py --apply    (tulis ke REST API JSON)
"""
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"}
OUT = "/home/mantapman/.hermes/profiles/atun/workspace"
MAP = f"{OUT}/tslaw_pemulihan/perbaikan_20261017"
PID = 3313

I1 = """<h2 class="wp-block-heading">Dasar Hukum Klausul Penyelesaian Sengketa di Indonesia</h2>
<p>Klausul penyelesaian sengketa bukan sekadar kebiasaan dalam penyusunan kontrak. Kebebasan para pihak menentukan isi perjanjian, termasuk cara menyelesaikan perselisihan, diakui Pasal 1338 ayat (1) KUH Perdata: semua persetujuan yang dibuat sesuai dengan undang-undang berlaku sebagai undang-undang bagi mereka yang membuatnya. Ayat (3) pasal yang sama menegaskan bahwa persetujuan harus dilaksanakan dengan itikad baik. Konsekuensi praktisnya, klausul yang sudah disepakati akan dipegang oleh hakim atau arbiter, sepanjang isinya tidak melanggar hukum, kesusilaan, dan ketertiban umum.</p>
<p>Untuk jalur arbitrase, rujukan utamanya adalah Undang-Undang Nomor 30 Tahun 1999 tentang Arbitrase dan Alternatif Penyelesaian Sengketa. Pasal 1 angka 1 mendefinisikan arbitrase sebagai cara penyelesaian sengketa perdata di luar peradilan umum yang didasarkan pada perjanjian arbitrase yang dibuat secara tertulis oleh para pihak yang bersengketa. Karena itu, kesepakatan arbitrase harus tertulis. Kesepakatan lisan tidak cukup sebagai dasar memindahkan sengketa dari pengadilan ke arbiter.</p>
<p>Pasal 5 ayat (1) UU 30/1999 membatasi objek arbitrase pada sengketa di bidang perdagangan dan mengenai hak yang menurut hukum dan peraturan perundang-undangan dikuasai sepenuhnya oleh pihak yang bersengketa. Ayat (2) pasal itu mengecualikan sengketa yang menurut peraturan perundang-undangan tidak dapat diadakan perdamaian. Bagi UMKM artinya jelas: sengketa yang sejak awal tidak bisa didamaikan tidak berubah menjadi sengketa arbitrase hanya karena kontraknya memuat klausul arbitrase.</p>
<p>Pasal 11 UU 30/1999 menegaskan bahwa perjanjian arbitrase tertulis meniadakan hak para pihak untuk mengajukan penyelesaian sengketa ke Pengadilan Negeri, dan pengadilan wajib menolak serta tidak campur tangan, kecuali dalam hal tertentu yang ditetapkan undang-undang. Ketentuan Pasal 3 senada: Pengadilan Negeri tidak berwenang mengadili sengketa para pihak yang telah terikat perjanjian arbitrase. Klausul arbitrase karena itu bersifat mengikat sekaligus menutup pintu litigasi.</p>
<p>Daya ikat putusannya diatur Pasal 60: putusan arbitrase bersifat final, mempunyai kekuatan hukum tetap, dan mengikat para pihak. Penjelasan pasal tersebut menyebut putusan arbitrase tidak dapat diajukan banding, kasasi, maupun peninjauan kembali. Bila pihak yang kalah tidak melaksanakan secara sukarela, Pasal 61 membuka jalan eksekusi melalui perintah Ketua Pengadilan Negeri atas permohonan salah satu pihak yang bersengketa. Sebelum itu, Pasal 59 mewajibkan lembar asli atau salinan otentik putusan diserahkan dan didaftarkan kepada Panitera Pengadilan Negeri dalam waktu paling lama 30 hari sejak putusan diucapkan.</p>
<p>Pada jalur non-arbitrase, perselisihan umumnya bermuara pada wanprestasi. Pasal 1243 KUH Perdata mengatur akibatnya berupa penggantian biaya, rugi, dan bunga karena tidak dipenuhinya suatu perikatan. Karena besaran ganti rugi sering menjadi perdebatan baru, klausul penyelesaian sengketa sebaiknya dilengkapi rumusan cara menghitung ganti rugi keterlambatan atau wanprestasi, bukan hanya menunjuk forum.</p>"""

I1B = """<h2 class="wp-block-heading">Contoh Rumusan Klausul Bertingkat</h2>
<p>Klausul yang rapi tidak langsung menunjuk pengadilan atau arbitrase. Ia menyusun tangga penyelesaian: musyawarah lebih dahulu, mediasi bila gagal, baru forum yang mengikat. Tujuannya agar biaya dan waktu naik bertahap, sejalan dengan besar kecilnya perselisihan yang benar-benar terjadi.</p>
<p>Contoh rumusan yang bisa dipakai sebagai titik awal: "Setiap perselisihan yang timbul dari Perjanjian ini diselesaikan secara bertahap: (a) musyawarah untuk mencapai mufakat dalam waktu 14 hari kalender sejak perselisihan diberitahukan secara tertulis; (b) apabila musyawarah tidak mencapai kesepakatan, para pihak menempuh mediasi dengan mediator yang disepakati dalam waktu 30 hari kalender; (c) apabila mediasi tidak menghasilkan kesepakatan, perselisihan diselesaikan melalui arbitrase dengan arbiter tunggal yang ditunjuk lembaga arbitrase yang disepakati, berkedudukan di Surabaya, dengan bahasa Indonesia sebagai bahasa arbitrase."</p>
<p>Satu kalimat penutup yang perlu ada: "Selama perselisihan berlangsung, para pihak tetap wajib melaksanakan kewajiban yang tidak dipersengketakan." Tanpa klausul ini, pihak yang berselisih bisa menghentikan seluruh prestasi dan memperbesar kerugian kedua belah pihak, termasuk kerugian pihak yang sebenarnya tidak bersalah.</p>
<p>Ada dua kesalahan yang sering muncul pada tahap ini. Pertama, menyebut dua forum berbeda di dua bagian kontrak, misalnya arbitrase pada pasal penyelesaian sengketa tetapi pengadilan pada pasal lain. Pertentangan seperti itu akan menjadi perdebatan tersendiri ketika sengketa muncul. Kedua, menyebut lembaga arbitrase tanpa kota kedudukan dan tanpa aturan acara, sehingga penunjukan arbiter menjadi kabur pada saat paling dibutuhkan.</p>"""

I1C = """<h2 class="wp-block-heading">Mediasi di Pengadilan dan Aturan yang Berlaku</h2>
<p>Jika kontrak memilih jalur pengadilan, mediasi bukan pilihan bebas. Pasal 4 ayat (1) Peraturan Mahkamah Agung Nomor 1 Tahun 2016 tentang Prosedur Mediasi di Pengadilan mewajibkan setiap sengketa perdata yang diajukan ke pengadilan lebih dahulu diupayakan penyelesaian melalui mediasi, kecuali ditentukan lain berdasarkan peraturan tersebut. Peraturan ini berlaku sejak 4 Februari 2016 dan mencabut Perma Nomor 1 Tahun 2008.</p>
<p>Jangka waktunya diatur Pasal 24: proses mediasi berlangsung paling lama 30 hari terhitung sejak penetapan perintah melakukan mediasi, dan atas dasar kesepakatan para pihak jangka waktu itu dapat diperpanjang paling lama 30 hari. Bagi UMKM, ketentuan ini memberi ruang agar sengketa bernilai tidak terlalu besar masih bisa selesai damai tanpa putusan hakim.</p>
<p>Perma 1/2016 juga menekankan iktikad baik. Pasal 7 mengatur konsekuensi bagi pihak yang dinilai tidak beriktikad baik dalam proses mediasi, antara lain gugatannya dinyatakan tidak dapat diterima oleh hakim pemeriksa perkara dan pihak tersebut dikenai kewajiban pembayaran biaya mediasi. Sejak 2022, mediasi dapat dilakukan secara elektronik berdasarkan Perma Nomor 3 Tahun 2022 tentang Mediasi di Pengadilan secara Elektronik yang berlaku 30 Mei 2022, sehingga pihak yang berkedudukan di luar kota tidak otomatis harus hadir secara fisik.</p>"""

I2 = """<h2 class="wp-block-heading">Arbitrase atau Pengadilan untuk Mitra di Luar Kota</h2>
<p>Ketika mitra usaha berkedudukan di provinsi lain, pilihan forum menentukan ongkos sejak awal. Klausul yang menunjuk pengadilan tertentu menyatukan tempat berperkara, sedangkan arbitrase memindahkan sengketa ke forum yang tidak terikat domisili tergugat.</p>
<p>Untuk sengketa yang berpotensi lintas negara, arbitrase punya kelebihan tambahan. Putusan arbitrase asing dapat diakui dan dilaksanakan di Indonesia melalui Convention on the Recognition and Enforcement of Foreign Arbitral Awards 1958 yang disahkan dengan Keppres Nomor 34 Tahun 1981. Instrumen itu tidak tersedia bagi putusan pengadilan Indonesia yang hendak dieksekusi di luar negeri, karena tidak ada perjanjian serupa untuk putusan pengadilan.</p>
<p>Sebaliknya, arbitrase menuntut biaya awal yang lebih besar, sebab honor arbiter dan biaya administrasi ditetapkan oleh lembaga arbitrase yang dipilih, bukan oleh negara. Untuk sengketa bernilai kecil, somasi tertulis disusul jalur pengadilan sering lebih hemat meski waktunya lebih panjang. Untuk sengketa bernilai besar, kerahasiaan dan kecepatan arbitrase umumnya sepadan dengan biayanya.</p>"""

I2B = """<h2 class="wp-block-heading">Daftar Periksa Sebelum Menandatangani Kontrak</h2>
<p>Sebelum kontrak ditandatangani, pastikan klausul penyelesaian sengketa memuat enam hal berikut. Daftar ini bisa dipakai langsung saat mereview draf yang dikirim mitra usaha.</p>
<ul><li><strong>Forum yang dipilih hanya satu rangkaian</strong> - sebut urutan musyawarah, mediasi, lalu arbitrase atau pengadilan, dan pastikan tidak ada pasal lain yang menunjuk forum berbeda.</li><li><strong>Lembaga arbitrase dan kota kedudukannya</strong> - tulis nama lembaga, kota, aturan acara yang dipakai, dan jumlah arbiter.</li><li><strong>Batas waktu setiap tahap</strong> - misalnya 14 hari untuk musyawarah dan 30 hari untuk mediasi, agar tidak ada pihak yang sengaja memperlambat.</li><li><strong>Bahasa dan hukum yang berlaku</strong> - untuk kontrak dengan mitra asing, tegaskan keduanya agar tidak muncul perdebatan tafsir.</li><li><strong>Pembagian biaya</strong> - atur siapa menanggung biaya mediasi, arbitrase, dan eksekusi, termasuk bila perkara berakhir damai.</li><li><strong>Kewajiban yang tetap berjalan</strong> - nyatakan bahwa prestasi yang tidak dipersengketakan tetap wajib dipenuhi selama proses penyelesaian berlangsung.</li></ul>
<p>Setelah kontrak berjalan, klausul ini masih bisa diperiksa ulang ketika addendum dibuat. Momen paling murah untuk memperbaiki klausul adalah saat kedua pihak masih ingin bekerja sama, bukan setelah sengketa muncul.</p>"""


def rest(path, data=None, timeout=180, method=None):
    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(4):
        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()[:400]
        except Exception as e:
            print(f"  (ulang {i+1}: {str(e)[:70]})")
            time.sleep(8)
    return 0, "gagal"


def bersih(html):
    t = re.sub(r"<(script|style)[^>]*>.*?</\1>", " ", html, flags=re.S | re.I)
    t = re.sub(r"<[^>]+>", " ", t)
    t = re.sub(r"&nbsp;?", " ", t)
    t = re.sub(r"&amp;", "&", t)
    return re.sub(r"\s+", " ", t).strip()


def sisip(teks, anchor, tambahan):
    n = teks.count(anchor)
    assert n == 1, f"anchor harus unik, ditemukan {n}: {anchor[:60]!r}"
    return teks.replace(anchor, tambahan + "\n" + anchor)


def main():
    apply = "--apply" in sys.argv
    st, p = rest(f"/wp-json/wp/v2/posts/{PID}?context=edit", method="GET")
    assert st == 200, (st, p)
    lama = p["content"]["raw"]
    print(f"# {PID} {p['title']['raw']}")
    print(f"  slug={p['slug']} status={p['status']} kata_lama={len(bersih(lama).split())}")
    meta_lama = {k: (p.get("meta") or {}).get(k) for k in
                 ("_yoast_wpseo_title", "_yoast_wpseo_metadesc", "_yoast_wpseo_focuskw")}
    print("  meta lama:", json.dumps(meta_lama, ensure_ascii=False)[:220])

    baru = lama
    a1 = '<h2 class="wp-block-heading">Mana yang Tepat untuk Bisnis Anda?</h2>'
    a2 = '<h2 class="wp-block-heading">Catatan Penting untuk UMKM</h2>'
    baru = sisip(baru, a1, I1 + "\n" + I1B + "\n" + I1C)
    baru = sisip(baru, a2, I2 + "\n" + I2B)

    tambahan = I1 + I1B + I1C + I2 + I2B
    # ---- pemeriksaan integritas ----
    assert "—" not in tambahan and "–" not in tambahan, "ada em/en dash di teks baru"
    assert not re.search(r"[“”‘’]", tambahan), "ada tanda kutip melengkung"
    assert not re.search(r"[\u4e00-\u9fff]", tambahan), "ada CJK"
    # semua blok lama wajib masih ada (normalisasi spasi + buang tag)
    def kalimat(h):
        t = re.sub(r"\s+", " ", bersih(h))
        return [s.strip() for s in re.split(r"(?<=[.?!])\s+", t) if s.strip()]
    kl, kb = kalimat(lama), kalimat(baru)
    hilang = [s for s in kl if s not in kb]
    print(f"  kalimat lama={len(kl)} masih ada={len(kl)-len(hilang)} hilang={len(hilang)}")
    assert not hilang, hilang[:3]
    harga_lama = len(re.findall(r"<p>", lama)); harga_baru = len(re.findall(r"<p>", baru))
    print(f"  kata_baru={len(bersih(baru).split())} | H2 {lama.count('<h2')} -> {baru.count('<h2')} | paragraf {harga_lama} -> {harga_baru}")
    assert len(bersih(baru).split()) >= 1000, "target 1.000 kata belum tercapai"

    open(f"{MAP}/post_{PID}_SEBELUM_LIVE.html", "w").write(lama)
    open(f"{MAP}/post_{PID}_SESUDAH_dryrun.html", "w").write(baru)
    open(f"{MAP}/post_{PID}_SEBELUM_LIVE.json", "w").write(json.dumps(p, ensure_ascii=False, indent=1))

    if not apply:
        print("  DRY RUN selesai. Jalankan dengan --apply untuk menulis.")
        return

    st, resp = rest(f"/wp-json/wp/v2/posts/{PID}", data={"content": baru}, method="POST")
    open(f"{MAP}/post_{PID}_POSTRESP.json", "w").write(json.dumps({"status": st, "resp": resp}, ensure_ascii=False, indent=1)[:4000])
    print(f"  POST -> {st}")
    assert st == 200, resp

    time.sleep(4)
    st, v = rest(f"/wp-json/wp/v2/posts/{PID}?context=edit", method="GET")
    assert st == 200, st
    raw = v["content"]["raw"]
    cek = {
        "id": v["id"],
        "isi_identik_dengan_yang_dikirim": raw == baru,
        "selisih_panjang": len(raw) - len(baru),
        "judul_sama": v["title"]["raw"] == p["title"]["raw"],
        "slug_sama": v["slug"] == p["slug"],
        "status": v["status"],
        "meta_sama": {k: (v.get("meta") or {}).get(k) for k in meta_lama} == meta_lama,
        "kata_sesudah": len(bersih(raw).split()),
        "H2_sesudah": raw.count("<h2"),
        "wa_me": len(re.findall(r"wa\.me/\d{6,}", raw)),
        "pilar": raw.count("/law-firm-surabaya"),
        "faqpage": raw.count("FAQPage"),
    }
    open(f"{MAP}/post_{PID}_VERIFIKASI.json", "w").write(json.dumps(cek, ensure_ascii=False, indent=1))
    open(f"{MAP}/post_{PID}_SESUDAH.html", "w").write(raw)
    print("  verifikasi:", json.dumps(cek, ensure_ascii=False)[:500])
    assert cek["isi_identik_dengan_yang_dikirim"] and cek["judul_sama"] and cek["slug_sama"] and cek["meta_sama"]


if __name__ == "__main__":
    main()
