#!/usr/bin/env python3
"""Perbaikan harian tslaw.id 2026-10-06 - post 2823
/6-langkah-mengurus-sengketa-tanah-secara-hukum-di-indonesia/

Prioritas 1: ada tayangan GSC (4) tetapi klik NOL, posisi 5,8 untuk kueri nyata
"cara mengurus kasus tanah tanpa biaya untuk ekonomi lemah". Judul H1 + judul SEO +
meta desc + fokus kw diselaraskan ke kueri itu, dan isi yang menjawabnya ditambahkan.
Prioritas 3: FAQ yang benar-benar tampil di halaman + schema FAQPage (dari Q&A itu).
Prioritas 5: 1 tautan kontekstual ke pilar /law-firm-surabaya/.
Bonus integritas: paragraf <p></p> kosong, 2 paragraf penutup yang menumpuk kata kunci
dan mengulang isi, sapaan "kamu" -> "Anda", "TSLaw" -> "TS Law", teks tombol WA.
Tombol WA sudah ada sejak awal (prioritas 4 tidak dikerjakan).
Semua fakta hukum dicek lewat Serper (UU 16/2011, Perma 1/2014, Pasal 237 HIR / 273 RBg,
Pasal 22 UU 18/2003) sebelum ditulis.

Jalankan tanpa argumen = dry run. Tambah --apply untuk menulis via REST (JSON).
"""
import base64, html as htmlmod, json, os, re, sys
import urllib.request, urllib.error

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
sys.path.insert(0, WS)
from cfg_tslaw import CFG

AUTH = base64.b64encode(f"{CFG['user']}:{CFG['pass']}".encode()).decode()
BASE = "https://tslaw.id/wp-json/wp/v2"
HDR = {"Authorization": f"Basic {AUTH}", "User-Agent": "Mozilla/5.0"}

PID = 2823
OUT = f"{WS}/tslaw_pemulihan/perbaikan_20261006"
os.makedirs(OUT, exist_ok=True)


def rest_get(path, timeout=90):
    req = urllib.request.Request(BASE + path, headers=HDR)
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.load(r)


def rest_post(path, payload, timeout=180):
    req = urllib.request.Request(
        BASE + path, data=json.dumps(payload).encode(),
        headers={**HDR, "Content-Type": "application/json"}, method="POST")
    try:
        with urllib.request.urlopen(req, timeout=timeout) as r:
            return r.status, json.load(r)
    except urllib.error.HTTPError as e:
        return e.code, e.read().decode()[:800]


def wc(s):
    t = htmlmod.unescape(re.sub(r"<(script|style).*?</\1>", " ", s, flags=re.S))
    return len(re.sub(r"<[^>]+>", " ", t).split())


p = rest_get(f"/posts/{PID}?context=edit")
raw = p["content"]["raw"]
open(f"{OUT}/post_{PID}_SEBELUM.html", "w").write(raw)
json.dump(p, open(f"{OUT}/post_{PID}_SEBELUM.json", "w"), ensure_ascii=False, indent=1)
KATA_SEBELUM = wc(raw)
print(f"[sebelum] kata={KATA_SEBELUM} judul={p['title']['raw']!r}")
print(f"[sebelum] seo={p['meta'].get('_yoast_wpseo_title')!r}")
print(f"[sebelum] meta={p['meta'].get('_yoast_wpseo_metadesc')!r} fokus={p['meta'].get('_yoast_wpseo_focuskw')!r}")

# ---------------- potong isi lama jadi bagian-bagian yang dipertahankan ----------------
i_btn = raw.index("<!-- wp:spectra/buttons")
i_baca = raw.index("<h2>Baca juga</h2>")
WA_BTN_SEBELUM = raw[i_btn:i_baca]
BACA = raw[i_baca:]
assert WA_BTN_SEBELUM.count("wa.me/6281909222026") == 1, "tombol WA tidak seperti seharusnya"
assert BACA.count("law-firm-surabaya") == 0

# bersihkan teks prefill tombol WA (nama lama + salah ketik) tanpa mengubah desain
WA_BTN = WA_BTN_SEBELUM.replace("Hi%20TSLaw%20Saya%20ngin%20konsultasi%20hukum",
                                "Halo%20TS%20Law%2C%20saya%20ingin%20konsultasi%20soal%20sengketa%20tanah")
assert WA_BTN != WA_BTN_SEBELUM and WA_BTN.count("wa.me/6281909222026") == 1

body = raw[:i_btn]
assert body.count("law-firm-surabaya") == 0, "pilar sudah ada, periksa dulu"

# buang paragraf kosong <p></p>
kosong = "<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->"
assert body.count(kosong) == 1, f"paragraf kosong: {body.count(kosong)}"
body = body.replace(kosong, "")

# buang 2 paragraf penutup yang menumpuk kata kunci (isinya dipindah ke intro baru)
awal_buang = body.index("<p>Cara mengurus sengketa tanah secara hukum ini atau")
awal_buang = body.rindex("<!-- wp:paragraph -->", 0, awal_buang)
akhir_buang = body.index("</p>", body.index("<p><strong>Tips Singkat:")) 
akhir_buang = body.index("<!-- /wp:paragraph -->", akhir_buang) + len("<!-- /wp:paragraph -->")
assert body.count("Tips Singkat") == 1
body = body[:awal_buang].rstrip() + "\n\n" + body[akhir_buang:].lstrip()

# intro lama (blok pertama) diganti intro baru
akhir_intro = body.index("<!-- /wp:paragraph -->") + len("<!-- /wp:paragraph -->")
INTRO_LAMA = body[:akhir_intro]
baca_intro_lama = htmlmod.unescape(re.sub(r"<[^>]+>", " ", INTRO_LAMA))
body = body[akhir_intro:].lstrip()

INTRO = """<!-- wp:paragraph -->
<p>Sengketa tanah termasuk perkara perdata yang paling sering muncul di Indonesia, dan salah satu yang paling melelahkan. Penyebabnya beragam: sertifikat ganda, batas tanah yang tidak jelas, jual beli yang cacat, warisan yang tidak dibagi adil, sampai penguasaan tanah tanpa hak. Kalau dibiarkan berlarut-larut, persoalannya makin rumit dan biaya yang harus dikeluarkan makin besar. Artikel ini merinci cara mengurus sengketa tanah secara hukum dalam enam langkah, sekaligus jalur bebas biaya bagi Anda yang tidak mampu menyewa advokat.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Secara garis besar ada dua jalur yang bisa ditempuh: non-litigasi (musyawarah dan mediasi) dan litigasi (pengadilan). Jalur pertama lebih murah, lebih cepat, dan menjaga hubungan baik dengan pihak lain. Jalur kedua memberi kepastian berupa putusan yang bisa dieksekusi. Dalam praktik, keduanya sering dipakai bertahap: coba mediasi lebih dulu, lalu ajukan gugatan kalau tidak ada kesepakatan.</p>
<!-- /wp:paragraph -->"""

# ---------------- bagian baru: jalur tanpa biaya (jawaban atas kueri nyata) ----------------
H2 = '<h2 class="wp-block-heading">%s</h2>'
H3 = '<h3 class="wp-block-heading">%s</h3>'

BAGIAN = []

BAGIAN.append("<!-- wp:heading -->\n" + H2 % "Cara Mengurus Kasus Tanah Tanpa Biaya untuk Masyarakat Ekonomi Lemah" + "\n<!-- /wp:heading -->")
BAGIAN.append("""<!-- wp:paragraph -->
<p>Biaya sering jadi alasan urusan tanah tertunda bertahun-tahun. Padahal ada beberapa jalur resmi yang tidak memungut biaya sepeser pun, mulai dari bantuan hukum yang dibiayai negara sampai pembebasan biaya perkara di pengadilan. Semuanya ditujukan untuk orang atau kelompok yang tidak mampu secara ekonomi, dan masing-masing punya syarat sendiri.</p>
<!-- /wp:paragraph -->""")

BAGIAN.append("<!-- wp:heading {\"level\":3} -->\n" + H3 % "Bantuan Hukum Gratis lewat UU Nomor 16 Tahun 2011" + "\n<!-- /wp:heading -->")
BAGIAN.append("""<!-- wp:paragraph -->
<p>Undang-Undang Nomor 16 Tahun 2011 tentang Bantuan Hukum mengatur bahwa negara menanggung biaya jasa hukum bagi orang atau kelompok orang miskin. Yang memberikan layanan adalah lembaga bantuan hukum atau organisasi kemasyarakatan yang lolos verifikasi dan akreditasi Kementerian Hukum, dengan anggaran dari APBN maupun APBD. Artinya pendampingan advokat untuk perkara tanah bisa Anda terima tanpa membayar honorarium.</p>
<!-- /wp:paragraph -->""")
BAGIAN.append("""<!-- wp:paragraph -->
<p>Syaratnya dua hal. Pertama, bukti tidak mampu secara ekonomi, umumnya surat keterangan tidak mampu dari lurah atau kepala desa, atau kartu jaminan sosial. Kedua, permohonan yang diajukan lisan atau tertulis kepada organisasi bantuan hukum terakreditasi di wilayah Anda. Organisasi itulah yang memverifikasi berkas lalu mendampingi perkara sampai tuntas, termasuk kalau akhirnya harus beracara di pengadilan.</p>
<!-- /wp:paragraph -->""")

BAGIAN.append("<!-- wp:heading {\"level\":3} -->\n" + H3 % "Posbakum: Konsultasi Gratis di Pengadilan Negeri" + "\n<!-- /wp:heading -->")
BAGIAN.append("""<!-- wp:paragraph -->
<p>Setiap Pengadilan Negeri punya Pos Bantuan Hukum (Posbakum). Layanannya mencakup informasi hukum, konsultasi atau nasihat hukum, bantuan menyusun dokumen hukum yang dibutuhkan, sampai informasi daftar organisasi bantuan hukum terdekat. Layanan ini diatur dalam Peraturan Mahkamah Agung Nomor 1 Tahun 2014 tentang Pedoman Pemberian Layanan Hukum bagi Masyarakat Tidak Mampu di Pengadilan.</p>
<!-- /wp:paragraph -->""")
BAGIAN.append("""<!-- wp:paragraph -->
<p>Satu hal yang perlu Anda tahu sejak awal: Posbakum memberi nasihat dan membantu menyiapkan dokumen, tetapi tidak mewakili Anda di ruang sidang. Begitu perkara masuk persidangan, pendampingan diambil alih organisasi bantuan hukum atau advokat.</p>
<!-- /wp:paragraph -->""")

BAGIAN.append("<!-- wp:heading {\"level\":3} -->\n" + H3 % "Pembebasan Biaya Perkara atau Prodeo" + "\n<!-- /wp:heading -->")
BAGIAN.append("""<!-- wp:paragraph -->
<p>Kalau gugatan tetap harus didaftarkan ke pengadilan, biaya perkaranya bisa dimintakan pembebasan. Permohonannya diajukan lisan atau tertulis dan dilampiri surat keterangan tidak mampu dari lurah atau kepala desa. Dasar hukumnya Pasal 237 HIR untuk wilayah Jawa dan Madura, dan Pasal 273 RBg untuk daerah lain. Bila dikabulkan, panjar biaya perkara dibebaskan. Peraturan Mahkamah Agung Nomor 1 Tahun 2014 juga mengenal sidang di luar gedung pengadilan supaya perkara bisa diperiksa lebih dekat ke tempat tinggal Anda.</p>
<!-- /wp:paragraph -->""")

BAGIAN.append("<!-- wp:heading {\"level\":3} -->\n" + H3 % "Bantuan Cuma-Cuma lewat Advokat (Pro Bono)" + "\n<!-- /wp:heading -->")
BAGIAN.append("""<!-- wp:paragraph -->
<p>Jalur terakhir adalah meminta bantuan cuma-cuma langsung ke kantor advokat. Pasal 22 Undang-Undang Nomor 18 Tahun 2003 tentang Advokat mewajibkan advokat memberi bantuan hukum secara cuma-cuma kepada pencari keadilan yang tidak mampu, jadi tidak ada salahnya menanyakan kuota pro bono saat berkonsultasi. Pilih kantor yang memang menangani sengketa pertanahan, misalnya <a href="https://www.tslaw.id/law-firm-surabaya/">law firm Surabaya</a> yang sehari-hari mendampingi perkara tanah, supaya berkas Anda tidak salah alur sejak awal.</p>
<!-- /wp:paragraph -->""")

# ---------------- sisipkan bagian baru sebelum Kesimpulan ----------------
KEPALA_KESIMPULAN = '<!-- wp:heading -->\n<h2 class="wp-block-heading">Kesimpulan</h2>\n<!-- /wp:heading -->'
assert body.count(KEPALA_KESIMPULAN) == 1, "kepala Kesimpulan tidak ditemukan"
body = body.replace(KEPALA_KESIMPULAN, "\n".join(BAGIAN) + "\n\n" + KEPALA_KESIMPULAN)

# tambah satu kalimat soal jalur bebas biaya di Kesimpulan
i_kes = body.index(KEPALA_KESIMPULAN)
i_tutup = body.index("</p>", i_kes)
KALIMAT_TAMBAHAN = (" Kalau biaya jadi kendala, jalur bantuan hukum gratis di atas bisa dipakai "
                    "tanpa mengorbankan kualitas pendampingan.")
body = body[:i_tutup] + KALIMAT_TAMBAHAN + body[i_tutup:]

# ---------------- FAQ (pertanyaan yang jawabannya sudah tampil di halaman) ----------------
FAQ = [
    ["Kalau tidak mampu bayar advokat, apa yang bisa saya lakukan?",
     "Ada empat jalur. Bantuan hukum gratis lewat UU Nomor 16 Tahun 2011, konsultasi Posbakum di Pengadilan Negeri, "
     "pembebasan biaya perkara atau prodeo dengan lampiran surat keterangan tidak mampu, dan permintaan bantuan "
     "cuma-cuma langsung ke kantor advokat sesuai Pasal 22 UU Nomor 18 Tahun 2003."],
    ["Apa yang paling penting disiapkan saat sengketa tanah muncul?",
     "Dokumen kepemilikan dan bukti penguasaan. Sertifikat, girik atau petuk pajak, akta jual beli, surat keterangan waris, "
     "bukti pembayaran PBB, plus foto dan keterangan saksi yang tahu riwayat tanahnya. Semakin lengkap, semakin kuat posisi Anda."],
    ["Apakah sengketa tanah wajib lewat mediasi dulu?",
     "Untuk perkara yang diperiksa di pengadilan, mediasi adalah tahap yang diwajibkan hakim sebelum masuk pokok perkara. "
     "Kalau mediasi berhasil, kesepakatan dituangkan dalam akta perdamaian yang mengikat kedua pihak. Kalau gagal, "
     "perkara dilanjutkan ke pemeriksaan pokok."],
    ["Tanah yang belum bersertifikat masih bisa disengketakan?",
     "Masih bisa. Tanah belum bersertifikat dibuktikan dengan girik atau petuk pajak, akta jual beli, bukti pembayaran PBB "
     "dari tahun ke tahun, surat ukur, dan keterangan saksi yang mengetahui riwayat kepemilikannya. Bukti-bukti itu yang "
     "dipakai menilai siapa berhak."],
    ["Kalau pihak yang kalah tidak mau mengosongkan tanah, bagaimana?",
     "Pihak yang menang mengajukan permohonan eksekusi ke pengadilan. Eksekusi dijalankan juru sita di bawah pengawasan "
     "ketua pengadilan negeri, dan bentuknya bisa pengosongan tanah, penyerahan tanah, atau pembayaran ganti rugi sesuai amar putusan."],
]

BAGIAN_FAQ = ['<h2>Pertanyaan yang Sering Diajukan</h2>']
for q, a in FAQ:
    BAGIAN_FAQ.append(f"<h3>{q}</h3>")
    BAGIAN_FAQ.append(f"<p>{a}</p>")

new = "\n".join([INTRO, "", body.rstrip(), "", WA_BTN.strip(), "", BACA.strip(), "",
                 "\n".join(BAGIAN_FAQ)]) + "\n"


def plain(s):
    return htmlmod.unescape(re.sub(r"<[^>]+>", "", s)).strip()


schema = {"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [
    {"@type": "Question", "name": plain(q), "acceptedAnswer": {"@type": "Answer", "text": plain(a)}}
    for q, a in FAQ]}
new += '<script type="application/ld+json">' + json.dumps(schema, ensure_ascii=False) + "</script>\n"

# ---------------- sapaan + nama kantor (aturan tetap) ----------------
new = re.sub(r"\bkamu\b", "Anda", new)
new = re.sub(r"\bKamu\b", "Anda", new)
new = new.replace("TSLaw", "TS Law")
# rapikan spasi ganda yang mungkin muncul
new = re.sub(r"[ \t]{2,}", " ", new)

# ---------------- judul + meta selaras kueri nyata ----------------
TITLE = "Cara Mengurus Sengketa Tanah Secara Hukum: 6 Langkah dan Jalur Tanpa Biaya"
SEO_TITLE = "Cara Mengurus Sengketa Tanah Tanpa Biaya: 6 Langkah"
FOKUS = "cara mengurus sengketa tanah"
META = ("Cara mengurus sengketa tanah secara hukum dalam 6 langkah, lengkap dengan jalur bebas "
        "biaya untuk masyarakat ekonomi lemah lewat Posbakum, LBH, dan prodeo.")

# ---------------- validasi ----------------
assert 120 <= len(META) <= 158, f"meta {len(META)} char di luar 120-158"
assert META.lower().startswith(FOKUS), "meta tidak diawali fokus kw"
assert len(SEO_TITLE) <= 60, f"seo title {len(SEO_TITLE)} char"
assert FOKUS in (SEO_TITLE + " " + META + " " + TITLE).lower(), "fokus kw tidak ada di judul/meta"
for tok in ["tanpa biaya", "ekonomi lemah", "posbakum", "prodeo", "6 langkah"]:
    assert tok in (SEO_TITLE + " " + META).lower(), f"token '{tok}' tidak ada di judul/meta"
assert "TSLaw" not in new, "nama lama TSLaw masih ada"
assert new.count("law-firm-surabaya") == 1, "tautan pilar bukan tepat 1"
assert new.count("wa.me/6281909222026") == 1, "tombol WA hilang/berubah"
assert new.count('"@type": "FAQPage"') == 1, "schema FAQPage tidak tepat 1"
assert len(re.findall(r"<h3>", new)) == len(FAQ) == len(schema["mainEntity"]), "FAQ tampil vs schema tidak sinkron"
assert not re.findall(r"\{[A-Za-z_\[\]0-9]+\}", new), "masih ada placeholder {..}"
assert not re.search(r"<p>[^<]*<p>", new), "masih ada <p> bersarang"
assert "<p></p>" not in new, "masih ada paragraf kosong"
assert "kamu" not in new.lower(), "sapaan 'kamu' masih ada"

# verifikasi tiap tautan internal di draf masih mengarah ke URL yang hidup
for url in sorted(set(re.findall(r'href="(https://www\.tslaw\.id/[^"]+)"', new))):
    assert url.endswith("/") and url.count(" ") == 0, f"tautan janggal: {url!r}"
    print("   tautan internal:", url)

for tag in ["p", "h2", "h3", "ul", "li", "div", "script"]:
    b = len(re.findall(rf"<{tag}[ >]", new)) - len(re.findall(rf"</{tag}>", new))
    assert b == 0, f"tag <{tag}> tidak seimbang ({b})"

KATA_SESUDAH = wc(new)
print(f"[sesudah] kata={KATA_SESUDAH} ({KATA_SESUDAH - KATA_SEBELUM:+d})")
print(f"[sesudah] H1={TITLE!r} ({len(TITLE)} char)")
print(f"[sesudah] seo={SEO_TITLE!r} ({len(SEO_TITLE)} char)")
print(f"[sesudah] meta={META!r} ({len(META)} char) | fokus={FOKUS!r}")
print(f"[sesudah] H2={len(re.findall(r'<h2', new))} H3={len(re.findall(r'<h3', new))} | "
      f"FAQ={len(FAQ)} | pilar=1 | WA=1 | schema=1")

open(f"{OUT}/post_{PID}_SESUDAH.html", "w").write(new)

if "--apply" not in sys.argv:
    print("\n[DRY RUN] tidak ada perubahan dikirim.")
    sys.exit(0)

kode, resp = rest_post(f"/posts/{PID}", {
    "title": TITLE, "content": new,
    "meta": {"_yoast_wpseo_focuskw": FOKUS, "_yoast_wpseo_title": SEO_TITLE,
             "_yoast_wpseo_metadesc": META}})
print(f"\nPOST /posts/{PID} -> HTTP {kode}")
if kode != 200:
    print(resp)
    sys.exit(1)
json.dump(resp if isinstance(resp, dict) else {"raw": resp},
          open(f"{OUT}/post_{PID}_POSTRESP.json", "w"), ensure_ascii=False, indent=1)
print("modified :", resp.get("modified"))
