#!/usr/bin/env python3
"""Klien HTTP mentah ke theskincodeclinic.com lewat `openssl s_client`.

Latar: sejak DPI memblokir ClientHello curl/urllib, semua request REST/XML-RPC
ke situs klien harus lewat CLI openssl. Modul ini menangani chunked encoding,
Basic auth, POST JSON, dan upload biner.

Pakai:
    from tsc_http import rest, rest_json, xmlrpc_call
    st, data = rest_json("GET", "/wp-json/wp/v2/posts?per_page=1&status=future")
"""
import base64
import importlib.util
import json
import subprocess
import tempfile
import time

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
_spec = importlib.util.spec_from_file_location("cfg", f"{WS}/cfg_tsc.py")
_m = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_m)
CFG = _m.CFG
# Host kanonis situs adalah www (non-www balas 404 dari cache Cloudflare).
HOST = "www." + CFG["domain"] if not CFG["domain"].startswith("www.") else CFG["domain"]
AUTH = base64.b64encode(f"{CFG['user']}:{CFG['pass']}".encode()).decode()
TRIES = 5
TIMEOUT = 90


def _parse(raw):
    head, _, rest = raw.partition(b"\r\n\r\n")
    lines = head.decode("utf-8", "replace").split("\r\n")
    if not lines or not lines[0].startswith("HTTP/"):
        raise RuntimeError(f"header HTTP tidak valid: {lines[:1]}")
    status = int(lines[0].split()[1])
    hdrs = {}
    for ln in lines[1:]:
        if ":" in ln:
            k, v = ln.split(":", 1)
            hdrs[k.strip().lower()] = v.strip()
    if hdrs.get("transfer-encoding", "").lower() == "chunked":
        buf, out = rest, b""
        while True:
            pos = buf.find(b"\r\n")
            if pos < 0:
                break
            size = int(buf[:pos].split(b";")[0], 16)
            if size == 0:
                break
            out += buf[pos + 2:pos + 2 + size]
            buf = buf[pos + 2 + size + 2:]
        rest = out
    elif "content-length" in hdrs:
        rest = rest[:int(hdrs["content-length"])]
    return status, hdrs, rest


def raw(method, path, headers=None, body=None, tries=TRIES, timeout=TIMEOUT):
    """Request HTTP/1.1 mentah lewat openssl s_client → (status, headers, payload)."""
    h = {"Host": HOST, "User-Agent": "Mozilla/5.0", "Accept": "*/*"}
    h.update(headers or {})
    if body is not None:
        h["Content-Length"] = str(len(body))
    lines = [f"{method} {path} HTTP/1.1"] + [f"{k}: {v}" for k, v in h.items()] + ["Connection: close"]
    req = ("\r\n".join(lines) + "\r\n\r\n").encode() + (body or b"")

    last = None
    for attempt in range(1, tries + 1):
        try:
            with tempfile.TemporaryFile() as errf:
                p = subprocess.Popen(
                    ["openssl", "s_client", "-quiet", "-connect", f"{HOST}:443", "-servername", HOST],
                    stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=errf)
                try:
                    out, _ = p.communicate(req, timeout=timeout)
                except subprocess.TimeoutExpired:
                    p.kill()
                    p.communicate()
                    raise RuntimeError("openssl s_client timeout")
            return _parse(out)
        except Exception as e:  # noqa: BLE001 — flake jaringan/DPI
            last = e
            time.sleep(1.5 * attempt)
    raise RuntimeError(f"transport gagal {tries}x {method} {path}: {last}")


def rest(method, path, data=None, extra_headers=None, auth=True, binary=None,
         content_type="application/json"):
    """Request REST WordPress. data = objek JSON, binary = bytes mentah."""
    headers = {"Authorization": f"Basic {AUTH}"} if auth else {}
    headers.update(extra_headers or {})
    body = None
    if binary is not None:
        body = binary
        headers["Content-Type"] = content_type
    elif data is not None:
        body = json.dumps(data).encode()
        headers["Content-Type"] = "application/json"
    return raw(method, path, headers, body)


def rest_json(method, path, data=None, **kw):
    """REST WordPress → (status, objek JSON / teks kalau gagal parse)."""
    st, hdrs, payload = rest(method, path, data=data, **kw)
    try:
        return st, json.loads(payload)
    except Exception:
        return st, payload.decode("utf-8", "replace")


def xmlrpc_call(method, params):
    """Panggil XML-RPC WordPress lewat transport yang sama."""
    import xmlrpc.client
    payload = xmlrpc.client.dumps(tuple(params), methodname=method,
                                  allow_none=True, encoding="utf-8").encode()
    st, _, body = raw("POST", "/xmlrpc.php",
                      {"Content-Type": "text/xml", "Authorization": f"Basic {AUTH}"}, payload)
    if st >= 400:
        raise RuntimeError(f"XML-RPC HTTP {st}: {body[:300]!r}")
    return xmlrpc.client.loads(body)[0][0]


if __name__ == "__main__":
    st, data = rest_json("GET", "/wp-json/wp/v2/posts?per_page=2&status=future&_fields=id,slug,date")
    print("status:", st)
    print(json.dumps(data, ensure_ascii=False)[:500])
