#!/usr/bin/env python3
"""
broadcast.py — admin announcements to every dashboard token holder,
now with images.

Usage (from the project root):

  Send a screenshot to EVERYONE (caption optional):
    python src\\execution\\broadcast.py "C:\\path\\to\\shot.png" --caption "Gold walls for today"

  Test to yourself first:
    python src\\execution\\broadcast.py "C:\\path\\to\\shot.png" --caption "Test" --to 1031071259

  Text-only announcement to everyone:
    python src\\execution\\broadcast.py --text "Dashboard maintenance at 21:00 UTC tonight."

Notes:
  - Captions support the same HTML tags as the other alerts (<b>, <i>, <a>).
  - Telegram caption limit is 1024 characters; plain-photo posts are fine.
  - PNG / JPG / WebP up to ~10 MB — a normal Windows Snipping Tool
    screenshot works as-is.
  - Recipients come from data\\logs\\dashboard_tokens.json (same list as
    the forecast alerts). Sends are spaced 0.3s apart.

Save to: src\\execution\\broadcast.py  (next to notify_forecast.py,
which it borrows the token plumbing from).
"""
import argparse
import ssl
import sys
import time
import urllib.request
import uuid
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
try:
    from notify_forecast import bot_token, send as tg_text, load_json, TOKENS_FILE
except Exception as e:
    sys.exit(f"could not import notify_forecast helpers: {e}")

MIMES = {"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg",
         "webp": "image/webp"}


def send_photo(token, chat_id, img_bytes, filename, caption):
    """Telegram sendPhoto via multipart/form-data (stdlib only)."""
    # SSL bypass — mirrors live_signal_monitor_v3 (required on this VPS:
    # Windows Python 3.14, self-signed certificate in the chain)
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE

    bd = uuid.uuid4().hex
    parts = []

    def field(name, value):
        parts.append((f'--{bd}\r\nContent-Disposition: form-data; '
                      f'name="{name}"\r\n\r\n{value}\r\n').encode("utf-8"))

    field("chat_id", str(chat_id))
    if caption:
        field("caption", caption)
        field("parse_mode", "HTML")
    ext = filename.rsplit(".", 1)[-1].lower()
    mime = MIMES.get(ext, "application/octet-stream")
    parts.append((f'--{bd}\r\nContent-Disposition: form-data; '
                  f'name="photo"; filename="{filename}"\r\n'
                  f'Content-Type: {mime}\r\n\r\n').encode("utf-8")
                 + img_bytes + b"\r\n")
    parts.append(f"--{bd}--\r\n".encode("utf-8"))
    body = b"".join(parts)

    req = urllib.request.Request(
        f"https://api.telegram.org/bot{token}/sendPhoto",
        data=body, method="POST",
        headers={"Content-Type": f"multipart/form-data; boundary={bd}"})
    with urllib.request.urlopen(req, timeout=30, context=ctx) as r:
        r.read()


def main():
    ap = argparse.ArgumentParser(description="Broadcast an image or text "
                                 "to all dashboard token holders.")
    ap.add_argument("image", nargs="?", help="path to a PNG/JPG screenshot")
    ap.add_argument("--caption", default="", help="caption under the image")
    ap.add_argument("--text", default="", help="text-only announcement")
    ap.add_argument("--to", default="", help="send only to this chat_id "
                    "(test mode)")
    a = ap.parse_args()

    if bool(a.image) == bool(a.text):
        sys.exit("give EITHER an image path OR --text \"message\"")

    img_bytes = None
    fname = ""
    if a.image:
        p = Path(a.image)
        if not p.exists():
            sys.exit(f"image not found: {p}")
        if p.suffix.lower().lstrip(".") not in MIMES:
            sys.exit("use a PNG, JPG or WebP image")
        img_bytes = p.read_bytes()
        if len(img_bytes) > 10 * 1024 * 1024:
            sys.exit("image is over Telegram's ~10 MB photo limit")
        fname = p.name
        if len(a.caption) > 1024:
            sys.exit("caption is over Telegram's 1024-character limit")

    token = bot_token()
    if not token:
        sys.exit("could not read the bot token from live_signal_monitor_v3")

    users = [u for u in load_json(TOKENS_FILE, {}).get("tokens", [])
             if u.get("chat_id")]
    if a.to:
        users = [u for u in users if str(u.get("chat_id")) == str(a.to)]
        if not users:
            users = [{"chat_id": a.to, "name": a.to}]
    seen = set()
    targets = []
    for u in users:
        cid = str(u["chat_id"])
        if cid not in seen:
            seen.add(cid)
            targets.append(u)
    if not targets:
        sys.exit("no recipients found in dashboard_tokens.json")

    sent = failed = 0
    for u in targets:
        name = u.get("name", u["chat_id"])
        try:
            if img_bytes is not None:
                send_photo(token, u["chat_id"], img_bytes, fname, a.caption)
            else:
                tg_text(token, u["chat_id"], a.text)
            sent += 1
            print(f"  sent -> {name}")
        except Exception as e:
            failed += 1
            print(f"  FAIL -> {name}: {e}")
        time.sleep(0.3)
    print(f"done: {sent} sent, {failed} failed "
          f"({'photo' if img_bytes is not None else 'text'})")


if __name__ == "__main__":
    main()
