#!/usr/bin/env python3
"""
chat-bootstrap-push — pre-tools snapshot to Rocky's Telegram.

Implements the chat-bootstrap skill (HQ-Vault/11_OpsMan_LFCS/Operations/skills/chat-bootstrap/SKILL.md)
as a scheduled push: reads vault state, formats per skill spec, sends to Telegram
so Rocky has fresh context on phone Claude before boarding the ute.

Schedule: daily 19:00 UTC (~05:00 AEST) via /etc/cron.d/chat-bootstrap-push.
"""
import os
import re
import subprocess
import sys
import urllib.parse
import urllib.request
from datetime import datetime, timedelta, timezone
from pathlib import Path

ROOT = Path("HQ-Vault")
DAILY = ROOT / "01_Daily"
HEALTH = ROOT / "_System" / "HealthLog.md"
OPEN_THREADS = ROOT / "10_RateRight" / "_Brain" / "Open-Threads.md"
RR_LOG = ROOT / "10_RateRight" / "_Brain" / "_Log.md"
LFCS_LOG = ROOT / "11_OpsMan_LFCS" / "_Brain" / "_Log.md"
ROADMAP = ROOT / "10_RateRight" / "ROADMAP.md"

NOW_UTC = datetime.now(timezone.utc)
AEST = timezone(timedelta(hours=10))
NOW_AEST = NOW_UTC.astimezone(AEST)
TODAY = NOW_AEST.strftime("%Y-%m-%d")
YESTERDAY = (NOW_AEST - timedelta(days=1)).strftime("%Y-%m-%d")
WEEK_AGO = (NOW_AEST - timedelta(days=7)).strftime("%Y-%m-%d")
TS_AEST = NOW_AEST.strftime("%Y-%m-%d %H:%M AEST")


def safe_read(path: Path, max_bytes: int = 40_000) -> str | None:
    if not path.exists():
        return None
    try:
        return path.read_text(encoding="utf-8", errors="ignore")[:max_bytes]
    except OSError:
        return None


def open_threads_summary(text: str | None) -> tuple[list[str], bool]:
    if not text:
        return [], False
    threads = []
    in_thread = False
    current = []
    for line in text.splitlines():
        if line.startswith("## "):
            if current:
                threads.append(" ".join(current).strip())
                current = []
            title = line[3:].strip()
            if title and not title.lower().startswith(("archive", "resolved")):
                current.append(title)
                in_thread = True
            else:
                in_thread = False
        elif in_thread and line.startswith("- "):
            current.append(line[2:].strip())
    if current:
        threads.append(" ".join(current).strip())
    return [t for t in threads if t][:8], True


def daily_one_liners(date: str) -> list[str]:
    f = DAILY / f"{date}.md"
    text = safe_read(f)
    if not text:
        return []
    lines = []
    for m in re.finditer(r"^## .+$", text, re.MULTILINE):
        header = m.group(0)[3:].strip()
        if header:
            lines.append(f"{date}: {header[:120]}")
    return lines[:5]


def healthlog_last_entry(text: str | None) -> str:
    if not text:
        return "no HealthLog data"
    headers = re.findall(r"(?:^|\n)## (.+)", text)
    if not headers:
        return "no HealthLog entries"
    return headers[-1].strip()


def roadmap_first_priorities(text: str | None) -> list[str]:
    if not text:
        return []
    lines = []
    in_section = False
    for line in text.splitlines():
        if line.startswith("## "):
            in_section = "CRITICAL" in line.upper() or "URGENT" in line.upper() or "NOW" in line.upper()
            continue
        if in_section and line.strip().startswith(("1.", "2.", "3.", "- ")):
            stripped = re.sub(r"^[\d\.\-\s]+", "", line.strip())[:140]
            if stripped:
                lines.append(stripped)
        if len(lines) >= 5:
            break
    return lines


def format_snapshot() -> str:
    open_text = safe_read(OPEN_THREADS)
    threads, threads_ok = open_threads_summary(open_text)
    if not threads_ok:
        threads = ["(Open-Threads.md not found)"]
    elif not threads:
        threads = ["(no open threads logged)"]

    today_lines = daily_one_liners(TODAY)
    yest_lines = daily_one_liners(YESTERDAY)
    week_lines = daily_one_liners(WEEK_AGO)
    activity = today_lines + yest_lines + week_lines
    if not activity:
        activity = ["(no daily entries in last week)"]

    roadmap_text = safe_read(ROADMAP)
    priorities = roadmap_first_priorities(roadmap_text)
    if not priorities:
        priorities = ["(ROADMAP empty/stale — see Open Threads)"]

    health_text = safe_read(HEALTH)
    health_line = healthlog_last_entry(health_text)

    gaps = []
    if not OPEN_THREADS.exists():
        gaps.append("Open-Threads.md missing")
    if not (DAILY / f"{TODAY}.md").exists():
        gaps.append(f"{TODAY} daily not yet created")
    if not health_text:
        gaps.append("HealthLog empty")
    if not gaps:
        gaps = ["(no gaps detected)"]

    # Truncate per-thread to keep message under Telegram 4096 cap
    threads = [t[:200] + ("…" if len(t) > 200 else "") for t in threads[:6]]

    block_lines = [
        f"VAULT SNAPSHOT {TS_AEST}",
        "=====================================",
        "",
        "## Open Threads (decisions waiting on you)",
    ]
    for t in threads:
        block_lines.append(f"- {t}")
    block_lines += ["", "## Recent activity"]
    for a in activity[:6]:
        block_lines.append(f"- {a[:140]}")
    block_lines += ["", "## Strategic priorities"]
    for p in priorities:
        block_lines.append(f"- {p}")
    block_lines += [
        "",
        "## Ops health",
        f"- Last HealthLog: {health_line}",
        "",
        "## Gaps noted",
    ]
    for g in gaps:
        block_lines.append(f"- {g}")
    block_lines += [
        "",
        "## Vault write path",
        f"Write to: HQ-Vault/01_Daily/{TODAY}.md (auto-syncs to git within 30 min)",
        "",
        "Session intent:",
        "_______________________________________________",
    ]
    return "\n".join(block_lines)


# Patterns matching common live secrets — masked before Telegram push.
# Vault content can legitimately mention these in Open-Threads.md (e.g. "the
# Stripe blocker"), but pushing them to Telegram (where phone history may
# leak if device lost/stolen) is a risk. Mask before send.
SECRET_PATTERNS = [
    re.compile(r"sk_live_[A-Za-z0-9]+"),
    re.compile(r"pk_live_[A-Za-z0-9]+"),
    re.compile(r"whsec_[A-Za-z0-9]+"),
    re.compile(r"sb_secret_[A-Za-z0-9]+"),
    re.compile(r"sb_sec_[A-Za-z0-9]+"),
    re.compile(r"sk-ant-api[A-Za-z0-9_\-]+"),
    re.compile(r"AIza[A-Za-z0-9_\-]{30,}"),  # Google API keys
    re.compile(r"eyJ[A-Za-z0-9_\-]{30,}\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+"),  # JWT
    re.compile(r"ghp_[A-Za-z0-9]+"),  # GitHub PAT
]


def mask_secrets(text: str) -> str:
    for pat in SECRET_PATTERNS:
        text = pat.sub(lambda m: m.group(0)[:10] + "…[MASKED]", text)
    return text


def load_env(path: str) -> dict:
    out = {}
    if not os.path.exists(path):
        return out
    for line in open(path, "r", encoding="utf-8", errors="ignore"):
        line = line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        k, _, v = line.partition("=")
        out[k.strip()] = v.strip().strip('"').strip("'")
    return out


def telegram_send(token: str, chat_id: str, text: str) -> bool:
    if not token or not chat_id:
        return False
    url = f"https://api.telegram.org/bot{token}/sendMessage"
    data = urllib.parse.urlencode({
        "chat_id": chat_id,
        "text": text,
        "disable_web_page_preview": "true",
    }).encode("utf-8")
    try:
        req = urllib.request.Request(url, data=data, method="POST")
        with urllib.request.urlopen(req, timeout=10) as resp:
            return resp.status == 200
    except Exception as e:
        print(f"telegram error: {e}", file=sys.stderr)
        return False


def emit_event(action: str, target: str, result: str, notes: str) -> None:
    try:
        subprocess.run(
            ["bash", "scripts/log-event.sh", "cron:chat-bootstrap-push", action, target, result, notes],
            check=False, timeout=5,
        )
    except Exception:
        pass


def main() -> int:
    snapshot = format_snapshot()
    # Mask any leaked secrets before pushing to Telegram (phone-loss risk).
    snapshot_safe = mask_secrets(snapshot)
    # Hard cap to stay under Telegram 4096 limit (with code-block overhead).
    if len(snapshot_safe) > 3900:
        snapshot_safe = snapshot_safe[:3900] + "\n…[truncated; full snapshot in vault]"
    # Telegram message: the snapshot wrapped in a code block so Rocky can copy
    # it cleanly into phone Claude.
    tg_msg = "```\n" + snapshot_safe + "\n```"

    env = load_env("/root/.hermes/.env")
    token = env.get("TELEGRAM_BOT_TOKEN", "")
    chat_id = env.get("TELEGRAM_HOME_CHANNEL", "")

    sent = telegram_send(token, chat_id, tg_msg)
    if sent:
        emit_event("cron_run", "chat-bootstrap-push.py", "ok", f"snapshot pushed ({len(snapshot)} chars)")
        print(f"chat-bootstrap-push: telegram pushed ({len(snapshot)} chars)")
    else:
        emit_event("cron_run", "chat-bootstrap-push.py", "fail", "telegram push failed")
        print("chat-bootstrap-push: telegram push failed", file=sys.stderr)
        return 1
    return 0


if __name__ == "__main__":
    sys.exit(main())
