#!/usr/bin/env bash
# Fix Claws "configured mailbox incomplete / failing IMAP" · Titan SSL
# - SSL pins 993/465
# - imap_subsonly=0 (list all folders · rebuild works)
# - empty incomplete folderlist → minimal IMAP root for re-discover
# - optional IMAP LIST via titan.pass to prove auth + preseed probe JSON
# - receive-all if session live
# NO polkit · SUDO not required for mail config
set -euo pipefail
export DISPLAY="${DISPLAY:-:0}"
export SUDO_PASS="${SUDO_PASS:-mememe}"
CLAWS="$HOME/.claws-mail"
RC="$CLAWS/accountrc"
FL="$CLAWS/folderlist.xml"
PASS_FILE="${TITAN_PASS_FILE:-$HOME/.config/biggrinrtx/titan.pass}"
ID_FILE="$HOME/.config/biggrinrtx/identity.json"
BACKUP="$CLAWS/backup-fix-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP"
[[ -f "$RC" ]] && cp -a "$RC" "$BACKUP/"
[[ -f "$FL" ]] && cp -a "$FL" "$BACKUP/"

USER_MAIL="biggrin@biggrinrtx.com"
if [[ -f "$ID_FILE" ]]; then
  USER_MAIL=$(python3 -c "import json;print(json.load(open('$ID_FILE')).get('user','biggrin@biggrinrtx.com'))" 2>/dev/null || echo "$USER_MAIL")
fi

echo "=== Claws mailbox fix · $(date -Is) ==="
echo "user=$USER_MAIL backup=$BACKUP"

# 1) SSL + IMAP sane defaults
python3 - <<PY
import re
from pathlib import Path
rc = Path.home()/".claws-mail"/"accountrc"
t = rc.read_text(errors="replace")
def sub(k,v):
    global t
    if re.search(rf"^{re.escape(k)}=", t, re.M):
        t = re.sub(rf"^{re.escape(k)}=.*$", f"{k}={v}", t, flags=re.M)
    else:
        t = t.rstrip()+f"\n{k}={v}\n"
# protocol 1 = IMAP
sub("protocol","1")
sub("receive_server","imap.titan.email")
sub("smtp_server","smtp.titan.email")
sub("ssl_imap","1")
sub("ssl_smtp","1")
sub("set_imapport","1")
sub("imap_port","993")
sub("set_smtpport","1")
sub("smtp_port","465")
sub("use_smtp_auth","1")
sub("user_id","$USER_MAIL")
sub("smtp_user_id","$USER_MAIL")
sub("imap_subsonly","0")  # list all folders · helps rebuild
sub("imap_directory","")
sub("receive_at_get_all","1")
# leave local MH inbox refs — IMAP uses folderlist for real tree
rc.write_text(t)
print("accountrc pins OK")
PY

# 2) Reset incomplete empty IMAP folderlist so Claws re-probes on connect
# Keep a minimal valid shell · claws recreates children after successful IMAP
python3 - <<PY
import re
from pathlib import Path
fl = Path.home()/".claws-mail"/"folderlist.xml"
user = "$USER_MAIL"
name = f"{user}@imap.titan.email"
# Valid Claws schema: <folder type="imap"> root only.
# Children must be <folderitem> (never nested <folder>) — populated after IMAP rebuild.
xml = f'''<?xml version="1.0" encoding="UTF-8"?>
<folderlist config_version="5">
    <folder type="imap" name="{name}" account_id="1" collapsed="0" sort="0" />
</folderlist>
'''
text = fl.read_text() if fl.is_file() else ""
# Count real <folder ...> nodes only (NOT the <folderlist> tag — old bug)
n_folders = len(re.findall(r"<folder[\s/>]", text))
n_items = len(re.findall(r"<folderitem[\s/>]", text))
if n_folders <= 1 and n_items == 0:
    fl.write_text(xml)
    print(f"folderlist: minimal IMAP root ({name}) · will rebuild on successful login")
else:
    print(f"folderlist: has {n_folders} folder + {n_items} folderitem nodes · left intact")
    print("  tip: right-click mailbox → Rebuild folder tree")
PY

# 3) Password probe · titan.pass OR decrypt Claws store (passkey0)
pass_get() {
  if [[ -n "${TITAN_PASS:-}" ]]; then printf '%s' "$TITAN_PASS"; return 0; fi
  if [[ -f "$PASS_FILE" ]]; then tr -d '\r\n' <"$PASS_FILE"; return 0; fi
  return 1
}

PROBE_JSON="$CLAWS/imap_list_probe.json"
IMAP_OK=0

python3 - <<'PY'
"""Probe Titan IMAP. Prefer titan.pass; else decrypt Claws passwordstorerc."""
import base64, hashlib, imaplib, json, os, re, ssl, sys
from pathlib import Path

user = os.environ.get("USER_MAIL") or "biggrin@biggrinrtx.com"
# re-read from identity inside script
id_file = Path.home() / ".config/biggrinrtx" / "identity.json"
if id_file.is_file():
    try:
        user = json.loads(id_file.read_text()).get("user") or user
    except Exception:
        pass

pass_file = Path(os.environ.get("TITAN_PASS_FILE") or Path.home() / ".config/biggrinrtx" / "titan.pass")
password = os.environ.get("TITAN_PASS") or ""
source = "env"
if not password and pass_file.is_file():
    password = pass_file.read_text().strip()
    source = "titan.pass"

def decrypt_claws() -> str | None:
    clawsrc = Path.home() / ".claws-mail" / "clawsrc"
    store = Path.home() / ".claws-mail" / "passwordstorerc"
    if not store.is_file() or not clawsrc.is_file():
        return None
    try:
        from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
        from cryptography.hazmat.backends import default_backend
    except ImportError:
        return None
    salt_b64 = rounds = None
    for line in clawsrc.read_text(errors="replace").splitlines():
        if line.startswith("master_passphrase_salt="):
            salt_b64 = line.split("=", 1)[1]
        if line.startswith("master_passphrase_pbkdf2_rounds="):
            try:
                rounds = int(line.split("=", 1)[1])
            except ValueError:
                rounds = 50000
    if not salt_b64:
        return None
    rounds = rounds or 50000
    salt = base64.b64decode(salt_b64)
    # use_master_passphrase=0 → PASSCRYPT_KEY "passkey0"
    key = hashlib.pbkdf2_hmac("sha1", b"passkey0", salt, rounds, dklen=32)
    blobs = re.findall(r"\{AES-256-CBC,\d+\}([A-Za-z0-9+/=]+)", store.read_text())
    if not blobs:
        return None
    raw = base64.b64decode(blobs[0])
    dec = Cipher(algorithms.AES(key), modes.CBC(b"\0" * 16), backend=default_backend()).decryptor()
    pt = dec.update(raw) + dec.finalize()
    pw = pt[16:].split(b"\0", 1)[0]
    try:
        s = pw.decode("utf-8")
    except Exception:
        return None
    return s if s.isprintable() and s else None

if not password:
    password = decrypt_claws() or ""
    source = "claws-store" if password else "none"

out = Path.home() / ".claws-mail" / "imap_list_probe.json"
if not password:
    print("WARN no TITAN_PASS / titan.pass / claws store password")
    print("  install -m 600 /dev/stdin ~/.config/biggrinrtx/titan.pass <<<'MAILBOX_PASS'")
    print("  Titan mailbox password (WP.com → Emails → Access Mail) — NOT WP.com login if separate")
    out.write_text(json.dumps({"ok": False, "error": "no_password", "user": user}, indent=2))
    sys.exit(0)

print(f"=== live IMAP LIST (source={source}) ===")
ctx = ssl.create_default_context()
try:
    M = imaplib.IMAP4_SSL("imap.titan.email", 993, ssl_context=ctx, timeout=25)
    M.login(user, password)
    typ, data = M.list()
    folders = []
    if typ == "OK" and data:
        for raw in data:
            if not raw:
                continue
            s = raw.decode(errors="replace") if isinstance(raw, bytes) else str(raw)
            folders.append(s)
    print(f"OK IMAP login · folders={len(folders)}")
    for f in folders[:30]:
        print(" ", f)
    out.write_text(json.dumps({"ok": True, "user": user, "source": source, "n": len(folders), "folders": folders[:50]}, indent=2))
    # persist working password for desk tools (mode 600)
    if source != "titan.pass":
        p = Path.home() / ".config/biggrinrtx" / "titan.pass"
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(password)
        p.chmod(0o600)
        print(f"wrote {p} (600) from working {source}")
    M.logout()
    sys.exit(0)
except Exception as e:
    err = f"{type(e).__name__}: {e}"
    print("FAIL IMAP", err)
    out.write_text(json.dumps({"ok": False, "user": user, "source": source, "error": err}, indent=2))
    # Stale WP.com password in Claws store is a common fail mode
    if "not allowed" in err.lower() or "AUTHENTICATIONFAILED" in err or "CNBF" in err:
        print("HINT: stored password rejected by Titan for IMAP.")
        print("  Use Titan mailbox password from WordPress.com → Emails → Access Mail")
        print("  (often different from the WP.com login password)")
        if source == "claws-store" or os.environ.get("CLAWS_CLEAR_BAD_PASS", "1") == "1":
            store = Path.home() / ".claws-mail" / "passwordstorerc"
            if store.is_file() and source == "claws-store":
                bak = store.with_suffix(".bak-badpass")
                bak.write_text(store.read_text())
                store.write_text("[config_version:5]\n\n[account:1]\n")
                store.chmod(0o600)
                print(f"cleared claws password store (backup {bak.name}) · Claws will prompt")
            # remove known-bad titan.pass
            if pass_file.is_file() and source == "titan.pass":
                pass_file.unlink()
                print("removed bad titan.pass")
    sys.exit(0)
PY

# 4) Online + receive if claws running
if pgrep -x claws-mail >/dev/null 2>&1; then
  claws-mail --online 2>/dev/null || true
  claws-mail --receive-all 2>/dev/null || true
  echo "signaled claws --online --receive-all"
else
  echo "claws not running · start: Projects/Santa/bin/claws-santa"
fi

echo "=== next in Claws UI ==="
echo "1. Close any incomplete-mailbox dialog"
echo "2. If prompted for password: Titan mailbox pass (Emails → Access Mail)"
echo "3. Folder list → right-click account root → Rebuild folder tree"
echo "4. Get Mail"
echo "DONE"
